WHY虚析构函数?
主要还是由于基类指针或引用可以不进行显示类型转换的情况下指向派生类对象。同时通过引用或者指针或引用调用方法时遵循:
- 如果该方法使用了关键字virtual,程序将根据引用或指针指向的类型选择方法;
- 否则,程序将根据引用或指针的类型选择方法。
例如:定义了基类Brass,派生类BrassPlus
如果ViewAcct()不是虚方法,则b1_ref.ViewAcct()和b2_ref.ViewAcct()均是使用Brass的方法。
如果ViewAcct()是虚方法,那么b1_ref.ViewAcct()使用Brass的方法,但b2_ref.ViewAcct()是使用BrassPlus的方法。
如果ViewAcct()是虚方法,那么b1_ref.ViewAcct()使用Brass的方法,但b2_ref.ViewAcct()是使用BrassPlus的方法。
如此,如果删除该引用或指针,在调用析构函数时遵循上述原则:
- 如果析构函数为虚函数,程序将根据引用或指针指向的类型选择方法,此时指针和引用若指向派生类,则调用该派生类的方法,然后自动调用基类析构函数。(建立派生类对象是,首先建立基类对象。对象删除顺序与建立顺序相反);
- 否则,程序将根据引用或指针的类型选择方法,此时指针和引用均为基类,故调用基类析构函数。
例如:基类Brass 的析构函数为虚函数,同时通过fullName 区分不同的对象。派生类BrassPlus每次调用析构函数时打印“BrassPlus”。通过两个析构函数的打印可以明确的看到那个对象在掉用了哪一个析构函数。
测试代码如下:
查看运行结果:
通过运行结果可以看出:在删除p_client[0] 时调用了基类Brass的析构函数,在删除p_client[1] 时,虽然其指针类型为Brass, 但是仍然首先调用了派生类BrassPlus 的析构函数,然后调用了对应的基类的析构函数。满足虚方法,程序根据指针指向的对象类型选择方法。
修改Brass 的析构函数为非虚函数,其他部分不变
查看运行结果:
通过运行结果可以看出:删除p_client[0]仍旧掉用了Brass 的析构函数,但是在删除p_client[1]时,并未调用BrassPlus 的析构函数,而直接调用了基类的析构函数。则派生类BrassPlus 对象并未被删除。
综上所述:基类必须有一个虚析构函数,即使该析构函数不执行任何操作。