## 说明
构造函数的调用顺序是先父类再子类。析构函数的顺序相反——先子类再父类。有继承关系的类的析构函数需要声明为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()