🦠

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