🦠
C++の基底クラスのデストラクタはvirtualにしろ
TL;DR
基底クラスのデストラクタにvirtual
をつけないと、継承したクラスのデストラクタが呼ばれない。
経緯
GMock
でモックオブジェクトが解放されない旨のエラーがでて困っていた。その原因がこれ。Interfaceクラスに仮想デストラクタを追加していなかった。
例
$ g++ example.cpp -o example
$ ./example
=== 悪い例 (非仮想デストラクタ) ===
BadBase constructor
BadDerived constructor (allocated memory)
BadBase destructor
=== 良い例 (仮想デストラクタ) ===
GoodBase constructor
GoodDerived constructor (allocated memory)
GoodDerived destructor (freed memory)
GoodBase destructor
sample.cpp
#include <iostream>
#include <memory>
// 悪い例: 非仮想デストラクタ
class BadBase {
public:
BadBase() { std::cout << "BadBase constructor" << std::endl; }
// virtual がない!
~BadBase() { std::cout << "BadBase destructor" << std::endl; }
virtual void someMethod() = 0;
};
class BadDerived : public BadBase {
private:
int *data;
public:
BadDerived() : data(new int[100]) {
std::cout << "BadDerived constructor (allocated memory)" << std::endl;
}
~BadDerived() {
delete[] data;
std::cout << "BadDerived destructor (freed memory)" << std::endl;
}
void someMethod() override {}
};
// 良い例: 仮想デストラクタ
class GoodBase {
public:
GoodBase() { std::cout << "GoodBase constructor" << std::endl; }
// virtual デストラクタ
virtual ~GoodBase() { std::cout << "GoodBase destructor" << std::endl; }
virtual void someMethod() = 0;
};
class GoodDerived : public GoodBase {
private:
int *data;
public:
GoodDerived() : data(new int[100]) {
std::cout << "GoodDerived constructor (allocated memory)" << std::endl;
}
~GoodDerived() {
delete[] data;
std::cout << "GoodDerived destructor (freed memory)" << std::endl;
}
void someMethod() override {}
};
int main() {
std::cout << "=== 悪い例 (非仮想デストラクタ) ===" << std::endl;
{
BadBase *obj = new BadDerived();
delete obj; // BadDerivedのデストラクタが呼ばれない!メモリリーク発生
}
std::cout << "\n=== 良い例 (仮想デストラクタ) ===" << std::endl;
{
GoodBase *obj = new GoodDerived();
delete obj; // 正しく両方のデストラクタが呼ばれる
}
return 0;
}
Discussion