C++ 的繼承與解構子的問題 (inheritance and destructor in C++)
範例繼承結構如下,有一個車子類別,卡車類別 繼承 車子類別。其中的 Run 函式是虛擬函式,用來實作多型。
車子類別
class CCar
{
public:
CCar();
~CCar();
virtual void Run();
};
CCar::CCar()
{
cout << "Car Constructor." << endl;
}
CCar::~CCar()
{
cout << "Car Destructor." << endl;
}
void CCar::Run()
{
cout << "Car Run." << endl;
}
卡車類別
class CCarTruck : public CCar
{
public:
CCarTruck();
~CCarTruck();
virtual void Run();
};
CCarTruck::CCarTruck() :CCar()
{
cout << "Truck Constructor." << endl;
}
CCarTruck::~CCarTruck()
{
cout << "Truck Destructor." << endl;
}
void CCarTruck::Run()
{
cout << "Truck Run." << endl;
}
主程式
#include <iostream>
#include "car.h"
using namespace std;
int main()
{
cout << "CAR----------------------" << endl;
CCar* pCar = new CCar;
pCar->Run();
delete pCar;
cout << endl;
cout << "TRUCK----------------------" << endl;
CCarTruck* pTruck = new CCarTruck;
pTruck->Run();
delete pTruck;
system("pause");
return 0;
}
CAR———————- Car Constructor. Car Run. Car Destructor. TRUCK———————- Car Constructor. Truck Constructor. Truck Run. Truck Destructor. Car Destructor. |
恩,沒有什麼問題,子類別的 constructor 會先執行父類別的 constructor。子類別的 destructor 會先執行自己的 destructor 再執行父類別的 destructor。順序是正確的。
但是我們稍微改一下程式碼,試試看把 Run 和 delete 包進函式裡。
主程式
#include <iostream>
#include "car.h"
using namespace std;
void CarRun(CCar* pCar)
{
pCar->Run();
delete pCar;
}
int main()
{
cout << "CAR----------------------" << endl;
CCar* pCar = new CCar;
CarRun(pCar);
cout << endl;
cout << "TRUCK----------------------" << endl;
CCarTruck* pTruck = new CCarTruck;
CarRun(pTruck);
system("pause");
return 0;
}
CAR———————- Car Constructor. Car Run. Car Destructor. TRUCK———————- Car Constructor. Truck Constructor. Truck Run. Car Destructor. |
咦~ Truck Destructor.不見了,也就是說子類別的 destructor 沒有執行,為什麼呢?
這是因為 CarRun 函式的參數是 CCar * 啊,所以 delete 時當然呼叫是 CCar 的 destructor 囉!
但是子類別可以轉成父類別存在不是多型正常的用法嗎?而且怎麼 Run 函式就可以執行到子類別的啊?
因為 Run 函式是 virtual 的啊!所以這問題要怎麼解決呢?那就把 destructor 改成 virtual 囉!
修改類別宣告
class CCar
{
public:
CCar();
virtual ~CCar(); //改成 virtual
virtual void Run();
};
class CCarTruck : public CCar
{
public:
CCarTruck();
virtual ~CCarTruck(); //改成 virtual
virtual void Run();
};
CAR———————- Car Constructor. Car Run. Car Destructor. TRUCK———————- Car Constructor. Truck Constructor. Truck Run. Truck Destructor. Car Destructor. |
恩,正常了。
結論:基底類別的 destructor 最好宣告成 virtual,以避免多型下產生的 destructor 問題。