デバッグ

バグの原因になりそうなところ、デバッグ手法について。

 

クラスのメモリコピー

クラスのメモリコピーにはバグの危険が潜んでいるのですが、何故それが危険なのかを知らない人が意外と多いようなので簡単に解説しておきます。

 

クラスのメモリコピーが危険とは書きましたが、全部が全部ダメというわけではありません。

内部でヒープ管理を行っているクラスの場合に限って、メモリコピーがバグの原因になることがあるのです。

 

例えば、コンストラクタでヒープ取得、デストラクタでヒープ解放するCHogeクラスがあったとします。

 

 
	// Hoge.h
	class CHoge
	{
	public:
	    CHoge();
	    CHoge(const CHoge& obj);
	    ~CHoge();
	 
	private:
	    char* m_pHoge;
	}
	// Hoge.cpp
	CHoge::CHoge()
	{
	    m_pHoge = new char[10];
	}
	 
	CHoge::CHoge(const CHoge& obj)
	{
	    m_pHoge = new char[10];
	    memcpy(m_pHoge, obj.m_pHoge, (sizeof(char) * ));
	}
	 
	CHoge::~CHoge()
	{
	    delete[] m_pHoge;
	}

 

さて、 このクラスをコピーしてみましょう。

 

	void func()
	{
	    CHoge hoge1 = CHoge();
	    CHoge hoge2;
	 
	 
	    //==== メモリコピー ====//
	    memcpy(&hoge2, &hoge1, sizeof(CHoge ));    // (a)これはNG
	 
	    /*~~ いろいろな処理 ~~*/
	 
	 
	    //==== コピーコンストラクタ ====//
	    hoge2 = hoge1;    // (b)こっちはOK
	 
	    /*~~ いろいろな処理 ~~*/
	 
	 
	    return;
	}

【コメント(a)部】

 

コメント(a)部分から見ていきます。

 

インスタンスの存在するメモリ領域が丸々コピーされることになりますが、m_pHogeに格納されるヒープのアドレスも全く同じになります。

 

 

・・・ということは?

 

 

 

そう。

 

例えば、hoge2のデストラクタが呼び出されヒープが解放された時点で、hoge1のm_pHogeの指すヒープ領域も無効となってしまいます。

 

そんな状態でhoge1がm_pHogeにアクセスしようとするとアクセスエラーが起こってしまいます。

【コメント(b)部】

 

こちらはコピーコンストラクタによるインスタンスのコピーです。

 

コピーコンストラクタ内でヒープ領域を取り直していますので、hoge2のデストラクタが呼び出されても、hoge1のヒープ領域が解放されることはありません。

 

 

 

以上。

 

クラスのメモリコピー時は上記のことに注意しましょう。

 

 

 

よく分からないという方は、"クラスをコピーしたい場合はメモリコピーをしない"と覚えておけば、とりあえずバグの心配はいりません(パフォーマンスの問題が出てくるかも知れませんが・・・)。

 

 

当然ながら、構造体にCHoge型のメンバが存在する場合に、構造体間でのメモリコピーすることもNGですので注意してください。