メンバ変数の初期化方法はいろいろあります。
①:メンバイニシャライザ
②:コンストラクタで代入
③:宣言時に初期化(VS2013以降のみ)
class cSample
{
private:
// ③
double m_X = 0.0;
double m_Y = 0.0;
public:
// ①
cSample() : m_X(0.0), m_Y(0.0)
{
// ②
m_X = 0.0;
m_Y = 0.0;
};
};
よく参考書には、「メンバイニシャライザを使用するべき」って書かれていることが多いですが、何が違うのか疑問だったので検証してみました。
以下、アセンブル表記
public:
cSample() : m_X(0.0), m_Y(0.0)
{
014BC470 push ebp
014BC471 mov ebp,esp
014BC473 sub esp,0CCh
014BC479 push ebx
014BC47A push esi
014BC47B push edi
014BC47C push ecx
014BC47D lea edi,[ebp-0CCh]
014BC483 mov ecx,33h
014BC488 mov eax,0CCCCCCCCh
014BC48D rep stos dword ptr es:[edi]
014BC48F pop ecx
014BC490 mov dword ptr [ebp-8],ecx
014BC493 mov eax,dword ptr [this]
014BC496 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC49E movsd mmword ptr [eax],xmm0
014BC4A2 mov eax,dword ptr [this]
014BC4A5 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC4AD movsd mmword ptr [eax+8],xmm0
class cSample
{
private:
double m_X = 0.0; //
double m_Y = 0.0; //
m_X = 0.0; //
014BC4F2 mov eax,dword ptr [this]
014BC4F5 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC4FD movsd mmword ptr [eax],xmm0
m_Y = 0.0; //
014BC501 mov eax,dword ptr [this]
014BC504 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC50C movsd mmword ptr [eax+8],xmm0
}
014BC551 mov eax,dword ptr [this]
014BC554 pop edi
014BC555 pop esi
014BC556 pop ebx
014BC557 mov esp,ebp
014BC559 pop ebp
014BC55A ret
慣れないと見づらいかと思いますが
public:
cSample() : m_X(0.0), m_Y(0.0)
{
014BC470 push ebp
014BC471 mov ebp,esp
014BC473 sub esp,0CCh
014BC479 push ebx
014BC47A push esi
014BC47B push edi
014BC47C push ecx
014BC47D lea edi,[ebp-0CCh]
014BC483 mov ecx,33h
014BC488 mov eax,0CCCCCCCCh
014BC48D rep stos dword ptr es:[edi]
014BC48F pop ecx
014BC490 mov dword ptr [ebp-8],ecx
014BC493 mov eax,dword ptr [this]
014BC496 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC49E movsd mmword ptr [eax],xmm0
014BC4A2 mov eax,dword ptr [this]
014BC4A5 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC4AD movsd mmword ptr [eax+8],xmm0
この部分はコンストラクタが実行され、
クラスのインスタンスが行われている(と思います。)
Cの構文の下にアセンブラが書かれているので分かりやすい。
実際にメンバ変数m_Xの初期化を行っているのが
014BC493 mov eax,dword ptr [this]
014BC496 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC49E movsd mmword ptr [eax],xmm0
この部分。3命令ですね。
thisで自分のポインタを取得し、xmm0に0を代入し、取得したポインタにxmm0をコピーしている感じでしょうか(適当)
ここでメンバイニシャライザによる初期化が行われていると思います。
そのすぐ下にあるのがメンバ変数m_Yの初期化
014BC4A2 mov eax,dword ptr [this]
014BC4A5 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC4AD movsd mmword ptr [eax+8],xmm0
m_Xの初期化とほとんど同じですが、3行目をみると[eax+8]となっています。
m_Xとm_Yは共にdouble型(8byte)のメンバで、メモリ的にはm_Xの後にm_Yがあると思います。
なので、単純に考えるとm_Yは先頭アドレスから8byte目なので、[eax+8]で8byte目を指定してそこを初期化しているような感じでしょうか(推測)
とりあえず、
この3行さえ分かれば、さっきのアセンブルで何をやっているか分かると思います。(というか、この3行以外は何をやってるか分からない・・・)
で、さっきの続きを見てみると・・・
class cSample
{
private:
double m_X = 0.0;
double m_Y = 0.0;
m_X = 0.0;
014BC4F2 mov eax,dword ptr [this]
014BC4F5 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC4FD movsd mmword ptr [eax],xmm0
m_Y = 0.0;
014BC501 mov eax,dword ptr [this]
014BC504 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC50C movsd mmword ptr [eax+8],xmm0
}
ここで気になるのは、以下の2つ
double m_X = 0.0;
double m_Y = 0.0;
アセンブラが書かれていませんね。
ちなみに、メンバイニシャライザが無い状態だとここで初期化が行われていました(さっきの3行がこの下にあった)
おそらく、メンバイニシャライザで初期化すると、③の方法による初期化はスキップされると思われます。
②の方法の初期化は、普通に代入していますね。
①と③があろうが、お構いなく代入しています。
さっきの3行とやってることは全く同じです。
double m_X = 0.0;
double m_Y = 0.0;
m_X = 0.0;
014BC4F2 mov eax,dword ptr [this]
014BC4F5 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC4FD movsd mmword ptr [eax],xmm0
m_Y = 0.0;
014BC501 mov eax,dword ptr [this]
014BC504 movsd xmm0,mmword ptr [__real@0000000000000000 (15395C8h)]
014BC50C movsd mmword ptr [eax+8],xmm0
}
①のメンバイニシャライザと②の代入初期化を両方ともやると、2回初期化が行われている模様。
③の宣言と同時に初期化の方法は、メンバイニシャライザがある場合は初期化されないという賢い初期化の模様。
よく「代入コストが~」と言われますが、見つからなかった・・・
もっと奥の方の問題だというのなら、もう分からない・・・
■結論
①と③の両方を使っていくのが良さそうですね。
ただ、③はVS2013でしか使えないのと、コンパイラによってアセンブラの処理は異なると思うので、一概には言い切れないところ。
実際に、その環境下でアセンブラを確認するのが確実かも。