プログラムTIPS

DEMO PROGRAM ヘキサドライブでは様々な研究開発をしています。その一部を紹介。

クラスのメンバ初期化時の注意事項(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の中身までゼロクリアされてしまうため、アドレスがゼロになっている関数を呼び出そうとした時点でクラッシュしてしまうのです。
(コンパイラの最適化オプション等の影響でクラッシュしない場合もあります)
 
ではどうやってメンバを初期化したら良いのかというと、やはりデフォルトコンストラクタや初期化メソッドで各メンバの初期値を入れるのが良いのではと思います。
 
クラスのメンバ初期化時には意識してみてくださいね。

【免責事項】

本サイトが提供している情報に関しては、安全性等、いかなる保証もされません。 株式会社ヘキサドライブは、これらの情報をあなたが利用することによって生ずるいかなる損害に対しても一切責任を負いません。

【著作権】

本サイトが提供しているコンテンツについては、特に断りのある場合を除き、株式会社ヘキサドライブが著作権を有します。