## 说明
构造函数的调用顺序是先父类再子类。析构函数的顺序相反——先子类再父类。有继承关系的类的析构函数需要声明为virtual,但并非必须。声明virtual表明函数不能再编译期间确定,只有在运行时才能确定。这样的场景是删除基类指针,但其指向是派生类。此时编译器看到的只有基类信息,如果没有声明virtual,就没有虚函数表或者虚函数表没有析构函数项,只能调用基类的析构函数。如果不声明virtual,将子类指针赋值给父类指针是有风险的操作。
## 实验代码
```C
/**
* @file constructor_destructor_sequence.cpp
* @brief 测试构造析构函数的调用次序
* @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()