基本クラスのポインタが、 new 演算子を使用して動的に生成された派生クラスのオブジェクトを指す場合があることがわかります (インターフェイスの役割と同様); 同時に、new 演算子を使用して動的に生成されたオブジェクトは、次のことを指すポインタを削除することで解放されます。それ。new 演算子を使用して動的に生成された派生クラス オブジェクトを基底クラス ポインタが指している場合、基底クラス ポインタを解放することでそのオブジェクトが解放される場合、プログラムが正しくない可能性があります。
たとえば、時間を維持するための時計基本クラス TimeKeeper があり、原子時計、水時計、腕時計などの一連のサブクラスがあります。ファクトリ モードに従って、原子時計を取得する getAtomicClock 関数を定義できます。クラス ポインタ TimeKeeper はそれらを指しますが、他のサブクラスも同様であり、この TimeKeeper はインターフェイスに相当します。
#include <iostream>
using namespace std;
class TimeKeeper
{
public:
TimeKeeper() {};
~TimeKeeper() { cout << "TimeKeeper::destrutor" << endl; }
};
// 原子钟
class AtomicClock : public TimeKeeper
{
~AtomicClock(){ cout << "AtomicClock::destrutor" << endl; }
};
class WaterClock : public TimeKeeper {}; // 水钟
class WristClock : public TimeKeeper {}; // 腕表
TimeKeeper* getAtomicClock()
{
return new AtomicClock;
}
TimeKeeper* getWaterClock()
{
return new WaterClock;
}
TimeKeeper* getWristClock()
{
return new WristClock;
}
int main() {
TimeKeeper* ptk = getAtomicClock();
delete ptk;
ptk = getWaterClock();
ptk = getWristClock();
}
上のコードに示されているように、3 つのファクトリ関数はすべて基本クラスによってポイントされています。オブジェクトの作成には new を使用するため、それらを解放するには当然 delete を使用します。しかし、ここで問題が発生します。ptk ポインターは次のとおりです。基本クラスがポイントします。サブクラスオブジェクトに追加したので、それを解放するとき、基本クラスのデストラクターを呼び出す必要がありますか、それともサブクラスのデストラクターを呼び出す必要がありますか? 答えは、基本クラスのデストラクターを呼び出すことです。これは静的コンパイルであるため、実行前にコンパイラーは ptk が TimeKeeper 型であることのみを知っているため、当然基本クラスのデストラクターを呼び出しますが、インスタンス化するのはサブクラスです。基本クラスのデストラクターを呼び出すと、サブクラスに基本クラスにない要素がある場合はどうなるでしょうか? 破壊されてメモリリークが起きているわけではないのでしょうか?
そこで、この問題を解決するには、仮想デストラクタの出番となり、基底クラスのデストラクタを仮想デストラクタとして宣言するだけで済みます。
virtual ~TimeKeeper() { cout << "TimeKeeper::destrutor" << endl; }
基本クラスとサブクラスの両方のデストラクターが呼び出されていることがわかりました。したがって、問題の鍵は、ポリモーフィズムを実装するときにヒープ領域のメモリがリークされる可能性があることであり、これには仮想デストラクタが必要であることを覚えておいてください。
参照の場合、ポリモーフィズムがヒープ領域にない場合でも、仮想的な破棄の必要はありません。
AtomicClock ac;
TimeKeeper& ptk = ac;
プログラムが終了しても、基本クラスとサブクラスのデストラクターは引き続き呼び出されます。
最後に、もう 1 つの原則を思い出してください。クラスが基本クラスとして使用されるように設計されていない場合、またはポリモーフィズムを持つように設計されていない場合は、仮想デストラクターを宣言すべきではありません。