C++ 初心者なのでかなりハマってしまった……。指摘等あればください、お願いします。
使用ツールは Visual Studio 2017 です。
注意
この記事の内容はあくまで推測なので、間違いの可能性があります
ご指摘いただきました
コメントに詳しい理由が書かれています。また、より実践的な例も指摘いただいております。
まとめ
-
ポリモーフィックに運用する基底クラスで、派生クラスがオブジェクトを保持する場合、基底クラスのデストラクタを virtual にする。
→ 派生先の保持するメンバクラスのデストラクタが呼ばれないため。
参考にさせて頂いたサイト
C++のメモリリーク検出!Visual Studio系
【c++】デストラクタにvirtualを付ける場合、付けない場合。
問題のコード
メモリリークするコードを提示します。
// メモリリーク検出用
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
#include <string>
// 基底クラス
class Base {
};
// 派生クラス
class Derived : public Base {
private:
std::string str;
};
int main() {
// メモリリークを検出する
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
Base* polymorphic = new Derived;
delete polymorphic;
}
上記のコードでは、派生クラスのstd::string str
が解放されず、メモリリークします。
派生クラスではデストラクタを使用しないため、基底クラスには virtual デストラクタを実装していません。が、これはおそらく誤りです
コメントで指摘いただきました
この場合、どうやら未定義動作となるようです。詳しくはコメントをご覧ください。
修正方法
修正済みのコードを提示します。
// メモリリーク検出用
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
#include <string>
// 基底クラス
class Base {
+ public:
+ virtual ~Base(){};
};
// 派生クラス
class Derived : public Base {
private:
std::string str;
};
int main() {
// メモリリークを検出する
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
Base* polymorphic = new Derived;
delete polymorphic;
}
上記のコードでは、基底クラスに virtual なデストラクタを実装しています。これにより、メモリリークが発生しません。
「基底クラスに virtual なデストラクタを実装しないと、派生クラスのデストラクタが呼ばれない」
と覚えていたのがダメだった。どうやら、派生クラスの持つメンバクラスのデストラクタも呼ばれないようです。
コメントで指摘いただきました
問題のコードの場合、どうやら未定義動作となるようです。詳しくはコメントをご覧ください。