建構子 和 虛擬函式 的問題

constructor 和 virtual function

來考慮一個 繼承 和 虛擬函式 的情況

//Class Car
class CCar
{
public:
	CCar();
	~CCar();
	
	virtual void Print();
	void TestPrint();
};

CCar::CCar()
{
	cout<<"CCar constructor."<<endl;
}
CCar::~CCar()
{
	cout<<"CCar destructor."<<endl;
}

void CCar::Print()
{
	cout<<"I'm CCar."<<endl;
}

void CCar::TestPrint()
{
	Print();
}


CTravel 類別

//Class CTravel
class CTravel : public CCar
{
public:
	CTravel();
	~CTravel();

	virtual void Print();
};

CTravel::CTravel() : CCar()
{
	cout<<"CTravel constructor."<<endl;
}

CTravel::~CTravel()
{
	cout<<"CTravel destructor."<<endl;
}

void CTravel::Print()
{
	cout<<"I'm CTravel."<<endl;
}

主程式

int main()
{
	CCar* pCar = new CCar;
	CCar* pTravel = new CTravel;
	pCar->Print();
	pTravel->Print();
	delete pCar;
	delete pTravel;

	system("pause");
	return 0;
}

執行結果

CCar constructor.
CCar constructor.
CTravel constructor.
I'm CCar.
I'm CTravel.
CCar destructor.
CCar destructor.

 

這樣很正常阿,沒有什麼好奇怪的阿!雖然 TestPrint 函式並不是 virtual 的,但是裡面呼叫的 Print 函式是 virtual 的,所以可以呼叫到子類別啊!

但是如果是在 constructor 裡呼叫 Print 呢?
對 CCar 類別做些修改

CCar::CCar()
{
	cout<<"CCar constructor."<<endl;
	Print();
}

主程式

void TestRun(CCar *p)
{
	p->TestPrint();
}
int main()
{
	CCar *pCar = new CCar;
	CCar *pTravel = new CTravel;
	TestRun(pCar);
	TestRun(pTravel);
	delete pCar;
	delete pTravel;
	system("pause");
	return 0;
}

執行結果

CCar constructor.
I'm CCar.
CCar constructor.
I'm CCar.
CTravel constructor.
CCar destructor.
CTravel destructor.
CCar destructor.

 

咦?為什麼沒有出現 I'm CTravel 呢?照理說,雖然是呼叫父類別的 constructor ,但也應該要呼叫到 virtual 的子類別 function 啊!

要知道這原因,就必須要先知道 virtual 是如何實作的,用 C 來說明 C++ 的 virtual function 的做法。

Car.h

typedef struct CCar
{
	struct CarVtbl* vtbl;	//宣告一個virtual function table
} CCar;

typedef CCar CTravel;

typedef struct CarVtbl CarVtbl;

struct CarVtbl
{
	void (*Destruct)(CCar* pCar);
	void (*Print)(CCar* pCar);
};

////////////////////////////////////////
//CCar
void Car_Construct(CCar* pCar);
void Car_Destruct(CCar* pCar);
void Car_Print(CCar* pCar);
CCar *Car_New();
////////////////////////////////////////
//CTravel
void Travel_Construct(CCar* pCar);
void Travel_Destruct(CCar* pCar);
void Travel_Print(CCar* sprite);
CCar *Travel_New();


Car.cpp

#include "car.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
////////////////////////////////////////
//CCar
CarVtbl carVtbl = 
{
	Car_Destruct,
	Car_Print,
};
CCar *Car_New()
{
	CCar* pCar;
	pCar = (CCar*) malloc(sizeof(CCar));		
	Car_Construct(pCar);
	return pCar;
}
void Car_Construct(CCar* pCar)
{
	pCar->vtbl = &carVtbl;		//在這裡指定virtual function table
	printf("CCar constructor.\n");
}
void Car_Destruct(CCar* pCar)
{
	pCar->vtbl = &carVtbl;
	printf("CCar destructor.\n");
}
void Car_Print(CCar* pCar)
{
	printf("I'm CCar.\n");
}
////////////////////////////////////////
//CTravel
CarVtbl travelVtbl = 
{
	Travel_Destruct,
	Travel_Print,
};
void Travel_Construct(CCar* pCar)
{
	Car_Construct(pCar);		//先呼叫父類別的constructor
	pCar->vtbl = &travelVtbl;	//然後才在這裡指定virtual function table
	printf("CTravel constructor.\n");
}
void Travel_Destruct(CCar* pCar)
{
	printf("CTravel destructor.\n");
	pCar->vtbl = &travelVtbl;
	Car_Destruct(pCar);
}
void Travel_Print(CCar* pCar)
{
	printf("I'm CTravel.\n");
}
CCar *Travel_New()
{
	CCar* pCar;
	pCar = (CCar*) malloc(sizeof(CTravel));		
	Travel_Construct(pCar);
	return pCar;
}

主程式

int main()
{
	CCar* pCar;
	CCar* pTravel;

	pCar = Car_New();
	pTravel = Travel_New();

	pCar->vtbl->Print(pCar);
	pTravel->vtbl->Print(pTravel);

	pCar->vtbl->Destruct(pCar);
	pTravel->vtbl->Destruct(pTravel);

	free(pCar);
	free(pTravel);

	system("pause");
	return 0;
}


執行結果

CCar constructor.
CCar constructor.
CTravel constructor.
I'm CCar.
I'm CTravel.
CCar destructor.
CTravel destructor.
CCar destructor.


嗯,跟第一個程式的結果一樣呢!那麼造成問題的地方在哪裡呢?來看看 2 個 struct 的 constructor。

void Car_Construct(CCar* pCar)
{
	pCar->vtbl = &carVtbl;		//在這裡指定virtual function table
	printf("CCar constructor.\n");
}
void Travel_Construct(CCar* pCar)
{
	Car_Construct(pCar);		//先呼叫父類別的constructor
	pCar->vtbl = &travelVtbl;	//然後才在這裡指定virtual function table
	printf("CTravel constructor.\n");
}

virtual function 之所以能夠運作,是因為 virtual function table 的關係,而子類別的 virtual function table 的指定是在呼叫父類別的 constructor 之後,所以如果在父類別的 constructor 裡面呼叫 virtual function 的話,會因為這裡的 virtual function table 還是父類別的,而不會呼叫到子類別的 virtual function。

相同的道理,在 destructor 也一樣。

結論:盡量不要在 constructor 和 destructor 裡面呼叫 virtual function。

arrow
arrow
    創作者介紹
    創作者 kamory 的頭像
    kamory

    伊卡洛斯之翼

    kamory 發表在 痞客邦 留言(0) 人氣()