## 說明
構造函數(shù)的調用順序是先父類再子類。析構函數(shù)的順序相反——先子類再父類。有繼承關系的類的析構函數(shù)需要聲明為virtual,但并非必須。聲明virtual表明函數(shù)不能再編譯期間確定,只有在運行時才能確定。這樣的場景是刪除基類指針,但其指向是派生類。此時編譯器看到的只有基類信息,如果沒有聲明virtual,就沒有虛函數(shù)表或者虛函數(shù)表沒有析構函數(shù)項,只能調用基類的析構函數(shù)。如果不聲明virtual,將子類指針賦值給父類指針是有風險的操作。
## 實驗代碼
```C
/**
* @file constructor_destructor_sequence.cpp
* @brief 測試構造析構函數(shù)的調用次序
* @copyright public domain
*/
#include <iostream>
class Base {
public:
Base() { std::cout << "Base()" << std::endl; }
~Base() { std::cout << "~Base()" << std::endl; }
};
class VBase {
public:
VBase() { std::cout << "VBase()" << std::endl; }
virtual ~VBase() { std::cout << "~VBase()" << std::endl; }
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived()" << std::endl; }
~Derived() { std::cout << "~Derived()" << std::endl; }
};
class VDerived: public VBase {
public:
Derived() { std::cout << "VDerived()" << std::endl; }
~VDerived() { std::cout << "~VDerived()" << std::endl; }
};
void test_0() {
std::cout <<"子類不聲明virtual,按基類指針刪除派生類" << std::endl;
VBase* p = new VDerived;
delete p;
}
void test_1() {
std::cout <<"不聲明virtual,按派生類指針刪除派生類" << std::endl;
Derived* p = new Derived;
delete p;
}
void test_2() {
std::cout <<"不聲明virtual,按基類指針刪除派生類" << std::endl;
Base* p = new Derived;
delete p;
}
void test_3() {
std::cout <<"不聲明virtual,按void*刪除派生類" << std::endl;
void* p = new Derived;
delete p;
}
int main() {
test_0();
test_1();
test_2();
test_3();
return 0;
}
```
## 運行及結果
> g++ constructor_destructor_sequence.cpp
constructor_destructor_sequence.cpp: In function 'void test_3()':
constructor_destructor_sequence.cpp:54:9: warning: deleting 'void*' is undefined [enabled by default]
> a.exe
子類不聲明virtual,按基類指針刪除派生類
VBase()
VDerived()
~VDerived()
~VBase()
不聲明virtual,按派生類指針刪除派生類
Base()
Derived()
~Derived()
~Base()
不聲明virtual,按基類指針刪除派生類
Base()
Derived()
~Base()
不聲明virtual,按void*刪除派生類
Base()
Derived()