本文へスキップ
BecomeCoder

C++コース · 第11章 継承とポリモーフィズム ― 型を広げる · レッスン48

仮想デストラクタと override / final

ブラウザで完結

導入

継承とポリモーフィズムには、正しく書かないと資源が漏れる落とし穴があります。それを防ぐ「仮想デストラクタ」と、ミスを減らす override / final を押さえて、この章を締めます。

説明

親の型のポインタで子オブジェクトdelete するとき、親のデストラクタが virtual でないと、子のデストラクタが呼ばれず後片付けが漏れます。多態的に使うクラスの親には、必ず仮想デストラクタ virtual ~親() {} を付けます。

flowchart TB
  a["親に virtual ~Base() あり<br/>delete で Derived の後片付けも実行(正しい)"]
  b["親に virtual なし<br/>Base の分しか片付かず資源が漏れる(危険)"]
  a --- b
#include <iostream>
using namespace std;

class Base {
public:
    virtual ~Base() { cout << "Base 片付け" << endl; }   // 仮想デストラクタ
};

class Derived : public Base {
public:
    ~Derived() override { cout << "Derived 片付け" << endl; }
};

int main() {
    Base* p = new Derived();
    delete p;   // 仮想なので Derived → Base の順で両方呼ばれる
    return 0;
}

override は「親の仮想関数を上書きしているつもり」を明示するキーワードです。もしスペルミスや引数違いで実際には上書きになっていないと、コンパイルエラーで教えてくれます。final はそれ以上の上書き(や継承)を禁止します。

class Base {
public:
    virtual void run() {}
};
class Mid : public Base {
public:
    void run() override final {}   // 上書きし、これ以上の上書きを禁止
};
// class Sub : public Mid { void run() override {}; };  // ← エラー(final)

まとめ

多態的に使うクラスの親には必ず仮想デストラクタを付けます。override は上書きミスをコンパイル時に発見でき、実務では上書きに必ず付けるのが推奨です。final は上書き・継承を禁止したいときに使います。

実際に動かしてみよう

下のエディタにC++を書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のC++コンパイラ(clang + libc++)がその場でコンパイルして実行し、出力が表示されます。std::string・vector・クラス・テンプレート・STL などモダンC++がそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(cin)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。