建構子 和 虛擬函式 的問題
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。