LoginSignup
20
17

More than 5 years have passed since last update.

クラスのメンバー初期化あれこれ

Posted at

メンバ変数の初期化方法はいろいろあります。
①:メンバイニシャライザ
②:コンストラクタで代入
③:宣言時に初期化(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でしか使えないのと、コンパイラによってアセンブラの処理は異なると思うので、一概には言い切れないところ。
実際に、その環境下でアセンブラを確認するのが確実かも。

20
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
17