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

C++の仮想デストラクタとは?基底クラスポインタから派生クラスを安全に削除する方法

C++では、基底クラスへのポインタを使って派生クラスのオブジェクトを削除する場合、基底クラスに仮想デストラクタ(virtual destructor)を定義する必要があります。

仮想デストラクタを定義していないと、deleteを実行したときに派生クラスのデストラクタが呼び出されず、メモリやリソースが正しく解放されない問題が発生します。これは、ポインタの静的な型(この場合は基底クラス)だけを見てデストラクタが選択されるためです。デストラクタをvirtualとして宣言すれば、実行時の実際のオブジェクト型に応じて適切なデストラクタが呼び出されるようになります。

サンプルコード

#include<iostream>
using namespace std;
class b {
    public:
        b() {
            cout<<"Constructing base \n";
        }
        virtual ~b() {
            cout<<"Destructing base \n";
        }
};
class d: public b {
    public:
        d() {
            cout<<"Constructing derived \n";
        }
        ~d() {
            cout<<"Destructing derived \n";
        }
};
int main(void) {
    d *derived = new d();
    b *bptr = derived;
    delete bptr;
    return 0;
}

実行結果

Constructing base
Constructing derived
Destructing derived
Destructing base

コードの解説

このプログラムの流れは以下の通りです。

まず、new d()によって派生クラスdのオブジェクトが動的に生成されます。オブジェクト生成時には、基底クラスのコンストラクタが先に呼ばれ、続いて派生クラスのコンストラクタが呼ばれるため、「Constructing base」「Constructing derived」の順に出力されます。

次に、生成されたオブジェクトのアドレスを基底クラス型のポインタbptrに代入し、delete bptr;でオブジェクトを解放しています。

ここで重要なのが、基底クラスbのデストラクタがvirtualとして宣言されている点です。これにより、delete時にポインタの型ではなく実際のオブジェクトの型が考慮され、まず派生クラスdのデストラクタが呼ばれ、その後で基底クラスbのデストラクタが呼び出されます。そのため、「Destructing derived」「Destructing base」という順序で出力されます。

仮想デストラクタがない場合の注意点

もし基底クラスのデストラクタがvirtualでなければ、delete bptr;は基底クラスbのデストラクタのみを呼び出します。この場合、派生クラスdのデストラクタは実行されず、派生クラス内で確保したリソースが解放されないままとなり、メモリリークにつながる可能性があります。この挙動はC++標準では未定義動作とされているため、ポリモーフィズムを利用して基底クラスポインタ経由でオブジェクトを扱うクラスには、必ず仮想デストラクタを定義することが推奨されます。

  1. C++のインライン仮想関数の仕組みと動作を解説

    C++における仮想関数(virtual function)は、基底クラスへのポインタや参照を使って、派生クラスの種類を意識することなく各クラスのメソッドを呼び出すための仕組みです。仮想関数はコンパイル時ではなく、実行時(ランタイム)に解決されます。仮想関数とインライン関数の役割仮想関数の主な目的は、実行時ポリモーフィズム(動的多態性)を実現することです。一方、インライン関数はコードの実行効率を高めるために使われます。インライン関数が呼び出されると、コンパイラはその呼び出し箇所に関数のコードを直接展開します。これにより、関数呼び出しのオーバーヘッドが削減されます。仮想関数がインライン化できる条件

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

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