マルチステートでの虚解析関数

2503 ワード

なぜ構造関数をvirtualと宣言するのですか?
なぜなら、deleteがベースクラスのポインタを指している場合、サブクラスのオブジェクトを指している場合、構造関数が虚しくないと、サブクラスの構造関数を呼び出すことができず、リソースが漏洩するためです.
クラスがマルチステートに使用される場合は、このvirtualが必要です.例:
#include 
using namespace std;

class Animal
{
    char* ap;
public:
    Animal()
    {
        ap = new char;
        std::cout << "Animal ctor" << std::endl;
    }
    virtual void foo()
    {
        std::cout << "Animal::foo" << std::endl;
    }
    virtual ~Animal()
    {
        std::cout << "Animal dtor" << std::endl;
        delete ap;
    }
};
class Dog : public Animal
{
    char* dp;
public:
    Dog()
    {
        dp = new char;
        std::cout << "Dog ctor" << std::endl;
    }
    virtual void foo()
    {
        std::cout << "Dog::foo" << std::endl;
    }
    virtual ~Dog()
    {
        delete dp;
        std::cout << "Dog dtor" << std::endl;
    }
};

int main()
{
    Animal* pa = new Dog();
    pa->foo();
    delete pa;
    return 0;
}

delete paは実際には次のようなものです.
pa->~Animal();

paが指すメモリを解放
ここで、~Animal()はvirtualであるため、Animalタイプのポインタで呼び出されたにもかかわらず、ダミーテーブルv-tableの情報に基づいて~Dog()が正しく呼び出される.virtualプロパティを削除すると,~Animal()が呼び出され,Dogクラスのコンストラクタが呼び出され,コンストラクタに割り当てられたリソースが解放されず,メモリリークが発生する.構造関数はデフォルトでvirtualと宣言され、この問題を回避できます.
ベースクラスの構造関数にvirtualを追加しない場合、実行効果は次のとおりです.
Animal ctor
Dog ctor
Dog::foo
Animal dtor

しかし、もう一つの問題はvirtualが必要ない場合があるということです.
クラスが継承されない場合、utilityクラスなど、クラスは完全に静的メソッドです.
あるいは、いくつかのクラスは継承される可能性がありますが、マルチステートとして使用されません.すなわち、構造関数以外にvirtualの方法はありません.この場合、virtual属性を削除することができます.
構造関数のvirtualプロパティを削除すると、このクラスには他のvirtual関数がないため、コンパイル時にv-tableが生成されず、コンパイル時間を節約し、最終的に生成されるプログラムのサイズを減らすことができます.さらに重要なのは、このルールに従って、クラスのメンテナンス者に情報を与えることです.すなわち、クラスはマルチステートクラスとして使用されるべきではありません.
同様に、抽象としてJavaのinterfaceを真似すると、次のような虚ベースクラスが宣言されます.
class AbstractBase
{
    virtual method1() = 0;
    virtual method2() = 0;
};

では、空のvirtual構造関数を追加する必要があります.
virtual ~AbstractBase(){}

COMに詳しい場合は、COMインターフェースにこのvirutal構造関数がないことに気づくかもしれません.これは、COMが参照カウントのメカニズムを使用してオブジェクトを維持するためである.COMオブジェクトを使用し終わったら、Release()を呼び出すと、COMの内部実装は参照技術がゼロであるかどうかをチェックします.次の場合に呼び出されます.
delete this;

Release()はvirtualなので、このCOMオブジェクトに対応する正しい派生クラスが呼び出され、delete thisは正しい解析関数を呼び出し、virtual解析関数を使用する効果が得られます.