MENU閉じる

LAB

C++プログラムTIPS

研究室プログラムTIPSC++2009.5.27

クラスのメンバ初期化時の注意事項

C++ でクラスのデータメンバ(メンバ変数)の値を初期化したい場合、
皆さんはどのように実装されていますか?
 
様々な方法が考えられると思いますが、
memset() の使用に関しては時として注意が必要になります!!

#include < string.h >
void *memset(void *s, int c, size_t n);

memset() は s で示されるメモリ領域の先頭から n バイトを c で埋める関数です。
 
文字を格納するバッファを特定の文字で埋めておきたい場合など、便利ですよね。

char buff[6];
memset(buff,'H',sizeof(buff));

同様にクラスオブジェクトに対しても memset() は使用できますが、
そのクラスが virtual メソッドを含んでいた場合は注意が必要です!

#include < string.h >
#include < memory.h >
#include < iostream >

class Hexa {
public:
Hexa(){}
~Hexa(){}
virtual void setStr(void){}

protected:
char _str[0x10];
};

class Hexa2 : pubilc Hexa {
public:
Hexa2(){}
~Hexa2(){}
void setStr(void){ strcpy(_str,"hexadrive"); }
char* drawStr(void){ return _str;}
};

int main(void)
{
Hexa2* pHexa = new Hexa2();
memset( (void*)pHexa, 0, sizeof(Hexa2) );
pHexa->setStr(); // ここでクラッシュする
cout<drawStr();
retuen 0;
}

このプログラムを実行するとsetStr()を呼び出す時点でクラッシュします。
なぜなら setStr() が virtual メソッドになっているからです。
 
C++ではクラスのメソッドに virtual キーワードをつけて宣言する(仮想関数)と、自動的に「関数ポインタの配列」(のポインタ)がメンバ変数領域に作られます。
これを仮想関数テーブル(vtable)と呼びます。
仮想関数テーブルの詳細はこちら
 
実行時に動的に呼び出すメソッドを決める必要がある、virtual メソッドはこのvtableで実現されています。
通常はプログラマーはvtableにはアクセスできませんが、memset()をvirtualメソッドを持ったクラスオブジェクトに適用してしまうと、上記例ではvtableの中身までゼロクリアされてしまうため、アドレスがゼロになっている関数を呼び出そうとした時点でクラッシュしてしまうのです。
(コンパイラの最適化オプション等の影響でクラッシュしない場合もあります)
 
ではどうやってメンバを初期化したら良いのかというと、やはりデフォルトコンストラクタや初期化メソッドで各メンバの初期値を入れるのが良いのではと思います。
 
クラスのメンバ初期化時には意識してみてくださいね。

RECRUIT

大阪・東京共にスタッフを募集しています。
特にキャリア採用のプログラマー・アーティストに興味がある方は下のボタンをクリックしてください

RECRUIT SITE