なぜ虚析构関数なのか.

2786 ワード

CPPをやりすぎると、継承に関する多くのインスタンスに触れることができます.継承システムでは、マルチステート特性のために多くのvirtualキーワードを使用することがよくあります.その中には普通のメンバー関数に対するvirtual修飾、継承関係に対するvirtual修飾、さらに多くの親クラスの構造関数に対するvirtual修飾があるが、なぜ親クラスの構造関数が虚であるのか.親クラスの構造関数をvirtualで修飾しないとどうなりますか?
まず、次の2つの例を見てみましょう.
class B
{
public:
	B(){ std::cout << "construct B !" << std::endl; }
	virtual ~B(){ std::cout << "destruct B ~" << std::endl; }

	virtual void Print(){std::cout << "Print for B @" << std::endl;}
};

class D : public B
{
public:
	D(){ std::cout << "construct D !" << std::endl; }
	~D(){ std::cout << "destruct D ~" << std::endl; }

	virtual void Print(){std::cout << "Print for D @" << std::endl;}
};

int main()
{
	B *b = new D;
	delete b;

	return 0;
}
実行結果:
construct B ! construct D ! destruct D ~ destruct B ~
ベースクラスの構造関数virtualを削除すると
class B
{
public:
	B(){ std::cout << "construct B !" << std::endl; }
	~B(){ std::cout << "destruct B ~" << std::endl; }

	virtual void Print(){std::cout << "Print for B @" << std::endl;}
};

class D : public B
{
public:
	D(){ std::cout << "construct D !" << std::endl; }
	~D(){ std::cout << "destruct D ~" << std::endl; }

	virtual void Print(){std::cout << "Print for D @" << std::endl;}
};

int main()
{
	B *b = new D;
	delete b;

	return 0;
}

実行結果は次のとおりです.
construct B ! construct D ! destruct B ~
多重状態の継承がある場合、親クラスの構造関数のVirtual修飾を失うと、オブジェクトを解放すると、サブクラスの構造関数が呼び出されないことが明らかになりました.クラスオブジェクトのリソース解放は、スタックメモリのfree、メンバーオブジェクトのプロファイルなど、構造関数で実現されることが知られています.構造関数が呼び出されずに漏れた場合、最終的にはサブクラスオブジェクトのクラスが持つリソースの漏れが明らかになります.したがって、Baseの解析関数を虚関数として宣言すると、Baseクラスのポインタで派生クラスオブジェクトを削除すると、派生クラスオブジェクトの解析関数が呼び出されるのは明らかです.マルチステート特性を持つベースクラスはvirtual構造関数を宣言する必要があります.
では、問題は、親クラスの構造関数を虚構造関数として宣言する必要がありますか?
虚析関数とは、すべてのclassクラスが必要ではありません.classにvirtual関数がある場合は、virtual構造関数を持つ必要があります.Classの設計目的はBase classとして使用されていないか,あるいは多態性を備えていない場合はvirtual構造関数を宣言すべきではない.では、なぜすべてのclassにvirtual構造関数を加えて万一を保証しないのでしょうか.実際に、必要でない場合は、構造関数にvirtualプロパティを追加しないでください.virtualはコストがかかるため、virtual関数を実装するには、クラスの間に虚ポインタVptrが虚関数テーブルを指すように追加する必要があります.これにより、クラスのボリュームが増大します.従来の判定ルールは、クラスにvirtualのメンバー関数がある場合にのみ、このベースクラスがマルチステートとして使用されていることを示し、構造関数をvirtualと宣言します.
PS:C++オブジェクトモデルでは、仮想関数の実装には、主に実行時にオブジェクトの所属とどの仮想関数を呼び出す必要があるかを動的に決定するために使用される追加の情報が必要であることが紹介されています.実装では、オブジェクトにvptrと呼ばれる虚ポインタを含ませることが多い.vptrは、vtbl(虚関数テーブル)と呼ばれる関数ポインタを含む配列を指します.このテーブルの最初のslotは、オブジェクトのタイプ情報であり、残りのsoltは関数ポインタです.虚関数を含むクラスごとにvtblが関連付けられています.虚関数がオブジェクトによって呼び出されると、オブジェクトのvptrが指すvtblが使用され、vtblで適切な関数ポインタが検索され、対応する特定の関数が呼び出されます.