c++クラス構造関数、構造関数と虚関数の間の些細なこと

4730 ワード

Q:クラスのコンストラクション関数は虚関数であってもよいか.
A:いいえ.
まず、設定後のコンパイルエラーで、文法上c++はクラスの構造関数を虚関数として宣言することを許さない.
次に,文法的な面を除いてクラスオブジェクトの構造は継承階層に従って上から下へ構築される.クラスオブジェクトを作成するときは、クラス全体に必要なストレージスペースを割り当ててから、コンストラクション関数の実行を開始します.簡単な例を挙げます.
class A
{
public:
	A() {}
	virtual void Print()
	{
		printf("this is A.
"); } }; class B : public A { public: B() {} virtual void Print() { printf("this is B.
"); } }; int main(int argc, char** argv) { B b; return 0; }

オブジェクトbが構築されると親Aのコンストラクション関数が先に実行され、親Aのコンストラクション関数が実行されると継承関係がどのようなものかは分からない.言い換えれば、親AはクラスBの存在を知らない.この場合、Aのコンストラクション関数が虚関数であれば、A自身のコンストラクション関数を呼び出すだけで、虚関数の多態性は現れない.
最後に,クラスの多態性は虚表ポインタによって実現されることを知っていたが,虚表ポインタは構造関数呼び出し時に初期化され,構造関数を虚関数に設定した.すなわち,このとき多態性を実現するには初期化された虚表ポインタが必要であるが,虚表ポインタはこのとき初期化されず,パラドックスを形成した.もう1つは、初期化された親クラス部分の虚表ポインタが親クラス自身の虚表を指していることであり、親クラス自身の虚表は親クラス自身の虚関数のポインタしか格納されず、表には子クラスの虚関数のポインタがないため、多態性を形成することはできない.
Q:コンストラクション関数は虚関数を呼び出すことができますか?
A:いいですよ.しかし、多態性は形成されない.
例を挙げます.
#include <stdio.h>
#include <typeinfo>
using namespace std;
class A;
typedef void(*Fun)(A*);

class A
{
private:
	int m_a;
public:
	A(int a) : m_a(a) 
	{
		//printf("%s
", typeid(*this).name()); //printf("this: %p
", this); printf("vptr: %p
", *(int*)this); printf("%p
", (int*)*((int*)(*(int*)this) + 1)); printf("%p
", (int*)*((int*)(*(int*)this) + 2)); ((Fun)*((int*)(*(int*)this) + 1))(this); ((Fun)*((int*)(*(int*)this) + 2))(this); printf("
"); } virtual ~A() { }; virtual void show() const { printf("show a: %d
", m_a); } virtual void disp() const { printf("disp a: %d
", m_a); } }; class B : public A { private: int m_b; public: B(int a, int b) : m_b(b), A(a) { //printf("this: %p
", this); printf("vptr: %p
", *(int*)this); printf("%p
", (int*)*((int*)(*(int*)this) + 1)); printf("%p
", (int*)*((int*)(*(int*)this) + 2)); ((Fun)*((int*)(*(int*)this) + 1))(this); ((Fun)*((int*)(*(int*)this) + 2))(this); } ~B() { } void show() const { printf("show b: %d
", m_b); } void disp() const { printf("disp b: %d
", m_b); } }; int _tmain(int argc, _TCHAR* argv[]) { A* pob3 = new B(100, 200); delete pob3; getchar(); return 0; }
の実行結果は次のとおりです.
vptr: 00C97860
00C91131
00C910C3
show a: 100
disp a: 100

vptr: 00C9795C
00C9107D
00C9115E
show b: 200
disp b: 200

以上から分かるように,親を構築する際に親が初期化した虚表ポインタは親自身の虚表を指し,子を構築する際に虚表を子の虚表に変更するので,構築関数呼び出し虚関数はエラーしないが多態性はない.
Q:解析関数は虚関数を呼び出すことができますか?
A:いいですが、同じように多態性はありません.
以下に例分析を示します.
#include <stdio.h>
#include <typeinfo>
using namespace std;
class A;
typedef void(*Fun)(A*);

class A
{
private:
	int m_a;
public:
	A(int a) : m_a(a) 
	{
	}

       virtual ~A()
	{
		printf("%s
", typeid(*this).name()); printf("this: %p
", this); printf("vptr: %p
", *(int*)this); ((Fun)*((int*)(*(int*)this) + 1))(this); ((Fun)*((int*)(*(int*)this) + 2))(this); printf("
"); }; virtual void show() const { printf("show a: %d
", m_a); } virtual void disp() const { printf("disp a: %d
", m_a); } void T() { show(); disp(); } }; class B : public A { private: int m_b; public: B(int a, int b) : m_b(b), A(a) { } ~B() { printf("%s
", typeid(*this).name()); printf("this: %p
", this); printf("vptr: %p
", *(int*)this); ((Fun)*((int*)(*(int*)this) + 1))(this); ((Fun)*((int*)(*(int*)this) + 2))(this); printf("
"); } void show() const { printf("show b: %d
", m_b); } void disp() const { printf("disp b: %d
", m_b); } }; int _tmain(int argc, _TCHAR* argv[]) { A* pob3 = new B(100, 200); delete pob3; getchar(); return 0; }

実行結果は次のとおりです.
class B
this: 003CE180
vptr: 00EF78B0
show b: 200
disp b: 200

class A
this: 003CE180
vptr: 00EF7860
show a: 100
disp a: 100

以上同様に,アナリシスAの場合,ダミーポインタはクラスBを指すダミーテーブルからクラスAを指すダミーテーブルに変わっているため,多態性を実現することはできない.
      (网友相関解释:コンパイラのやり方は、分析子类が完成した后に、父类のオブジェクトの虚関数表を回复して、この时の子类のオブジェクトの対応する父类のオブジェクトの虚関数表、すでに父类の虚関数表で、この时虚関数を呼び出して、正常な虚関数と同じではありませんて、父类のオブジェクトは自分の虚関数を呼び出すことしかできなくて、虚関数の呼び出しの方式ではありません同じです.)
Q:解析関数を虚関数にするのはなぜですか?
A:クラスの継承において、ベースクラスポインタが派生クラスを指す場合、ベースクラスポインタdeleteを使用する場合、虚関数として定義しないと、派生クラスの派生部分は解析できません.