C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++の純粋仮想デストラクタとは?仕組みと実装例を解説


純粋仮想デストラクタとは

C++では、デストラクタを純粋仮想関数として宣言することが可能です。ただし、クラスに純粋仮想デストラクタが含まれる場合は、必ずその関数本体(実装)を提供しなければなりません。実装を省略すると、リンク時にエラーが発生します。

これは、オブジェクトが破棄される際に、派生クラスのデストラクタから基底クラスのデストラクタが必ず呼び出されるためです。純粋仮想として宣言していても、この呼び出しに応えるための実体が必要になります。

サンプルコード

#include <iostream>
using namespace std;

class B {
public:
    virtual ~B() = 0; // 純粋仮想デストラクタ
};

B::~B() {
    std::cout << "Pure virtual destructor is called";
}

class D : public B {
public:
    ~D() {
        cout << "~D() is executed" << endl;
    }
};

int main() {
    B* bptr = new D();
    delete bptr;
    return 0;
}

実行結果

~D() is executed
Pure virtual destructor is called

コードの解説

  • Bクラスvirtual ~B() = 0; によって純粋仮想デストラクタを持ち、抽象クラスとなっています。同時に、クラス定義の外で B::~B() の実装を提供している点がポイントです。
  • Dクラス:Bを継承し、独自のデストラクタ ~D() を定義しています。
  • main関数:基底クラスBのポインタ経由でDのオブジェクトを生成し、delete で解放しています。

delete bptr; が実行されると、まず派生クラスDのデストラクタ ~D() が呼び出され、その後に基底クラスBのデストラクタ ~B() が呼び出されます。実行結果の出力順序は、この呼び出しの流れをそのまま表しています。

なぜ関数本体が必要なのか

通常の仮想関数と異なり、デストラクタは派生クラスでオーバーライドされていても、破棄処理の最後に必ず基底クラスのデストラクタが呼び出されます。そのため、純粋仮想デストラクタであっても実体が存在しないと、リンカが必要なシンボルを見つけられずエラーとなります。

主な利用シーン

クラスを抽象クラスとして扱いたいものの、純粋仮想関数に適したメンバ関数が他に存在しない場合などに、純粋仮想デストラクタが活用されます。インターフェースクラスの設計においても有効なテクニックです。


  1. C++の純粋関数とは?特徴とstrlen()・sqrt()を使った実装例をわかりやすく解説

    純粋関数(Pure Function)とは、同じ引数の値に対して常に同じ結果を返す関数のことです。純粋関数は結果を返すだけにとどまり、引数の変更、入出力ストリームへのアクセス、出力生成といった余計な副作用(Side Effect)を一切持ちません。この性質により、純粋関数はプログラムの動作が予測しやすく、テストやデバッグ、並列処理においても扱いやすいという大きなメリットがあります。純粋関数と非純粋関数の例C++標準ライブラリには、純粋関数として振る舞うものが多数存在します。代表的な例としては以下のような関数が挙げられます。純粋関数の例: sin()、strlen()、sqrt()、max()、

  2. C++で仮想デストラクタを使うべき場面とは?ポリモーフィックな基底クラスの注意点

    『Effective C++』におけるスコット・マイヤーズの助言 スコット・マイヤーズは名著『Effective C++』の中で、仮想デストラクタについて次のように述べています。 「クラスが仮想関数をひとつでも持つなら、そのクラスは仮想デストラクタを持つべきである。一方、基底クラスとして設計されていないクラスや、ポリモーフィックに扱われることを意図していないクラスは、仮想デストラクタを宣言すべきではない」 つまり、ポリモーフィックな基底クラスでは、デストラクタを必ず仮想関数として宣言するのが原則です。 仮想デストラクタがないと何が起きるのか 問題が発生するのは、派生クラスのコンストラクタでオ