レバテックフリーランスのサイトに当サイトが紹介されました!

[C++]コンテナへ配列いり構造体を挿入したときの挙動

配列入りの構造体を定義してそれをコンテナへ挿入したとき、

どんな挙動になるか正確に把握してますか?

ワタシは不安になっちゃいました。

というか、実行した結果、コピーされてたんで思惑通りなんでいいんですけど、

どうゆうフローでそうなってるか把握しておかないと気持ち悪い!

そんなわけで検証です。

こんな構造体を定義

struct Data
{
    int id = 0;
    int padding;
    char name[32] = { '\0' };

    Data()
    {}

    Data(int _id, const char* _name) 
        : id(_id)
    {
        strcpy_s(name, _name);
    }
};

こんな感じでコンテナへ挿入


using TestVector = vector;
int main()
{
    TestVector vector;
    Data data(1, "test");
    vector.push_back(data);

    auto& cData = *(vector.begin());
    cout << "name = " << cData.name << ", id = " << cData.id << endl;
    cout << "&data.name = " << hex << &data.name << ", &cData.name = " << hex << &cData.name << endl;

    return 0;
}

結果
name = test, id = 1
&data.name = 000000C24053FC50, &cData.name = 000001D35F5CBAD8

結果、コンテナの中のインスタンスは、別のアドレスの配列を持ちながらしっかりと内容はコピーされているわけです。

以下ざっくりとフロー解説

まず、コンテナ挿入時にインスタンスを引数とした場合、コピーコンストラクタが呼ばれます。

これはコピーコンストラクタを明示的に定義してブレイクすればすぐに確認できます。

なので、状況を整理すると、最終的に知りたいことは、
コピーコンストラクタでの配列の扱いってどうなってるの? ということ。

ちなみに、「こちらのサイト」でも記載されているとおり、ただのmemcpyではないとの定義があります。それぞれのコピーコンストラクタまたは代入演算子を呼んでくれると。

ちなみに、以下の①のときはコピーコンストラクタが呼ばれます。②のときはコンストラクタが呼ばれて代入演算子が呼ばれます。


Data copy1 = data;    //①

Data copy2;           //②
copy2 = data;         //②

となると、ますます
配列ってどうなるん?
配列って代入で丸コピーなんてできないやん?
配列のコピーコンストラクタなんてあるん?
とワタシはなります。

ま、結果が物語っているのでコピーしてくれる に決まっているんですが ^^;

なんでコピーしてくれちゃうの?デフォルトのコピーコンストラクタの処理ってどうなってるの?
というのを解釈するため、コピーコンストラクタ呼び出し部分を逆アセンブルしてみます。

すると以下のようになります。

Data copy = data;   // コピーコンストラクタ確認用処理

00007FF737C24779 lea rax,[copy]
00007FF737C24780 lea rcx,[data]
00007FF737C24787 mov rdi,rax
00007FF737C2478A mov rsi,rcx
00007FF737C2478D mov ecx,28h
00007FF737C24792 rep movs byte ptr [rdi],byte ptr [rsi]

このアセンブラは


copyインスタンスのアドレスをraxレジスタにロード
dataインスタンスのアドレスをrcxレジスタにロード
raxレジスタの内容をrdiにコピー
rcxレジスタの内容をrsiにコピー
28(hexなので10進数だと40)をecxレジスタにコピー
byteコピーをecxの回数繰り返す

という意味。

 

すなわち、デフォルトのコピーコンストラクタは(今回の場合の最適化の結果として)

「結局memcpyと同じ処理」
をしてるんですね。

中身にコピーコンストラクタなどが定義されたクラスインスタンスなどが存在すればアセンブラの内容も変わってました。

意外と明確にこの辺を書いてくれてる記事がなかったりする。

C++の本」 を読めば書いてあるかも。(会社に置いてあるから今度見る・・)

というわけで、
デフォルトのコピーコンストラクタ呼び出しによって配列はしっかりコピーされるのでした。
(もちろん、const char* とかのポインタで保持させた場合はアドレスコピーだからね!)

そいではこのへんで。

コメント

タイトルとURLをコピーしました