继承关系对基类拷贝控制最直接的影响是基类通常应该定义一个虚析构函数,这样我们就能动态分配继承体系中的对象了。
如前所述,当我们delete一个动态分配的对象的指针时将执行析构函数。如果该指针指向继承体系中的某个类型,则由可能出现指针的静态类型与被删除的动态类型不符的情况。例如,如果我们delete一个Quote*类型的指针,则该指针有可能实际指向了一个Bulk_quote类型的对象。如果这样的话,编译器就必须清楚它应该执行的是Bulk_quote的析构函数。和其它函数一样,我们通过在基类中将析构函数定义成虚函数以确保执行正确的析构函数版本:
class Quote { //如果我们删除的是一个指向派生类对象的基类指针,则需要虚析构函数 virtual ~Quote() = default; //动态绑定析构函数 };
和其它虚函数一样,析构函数的虚属性也会被继承。因此,无论Quote的派生类使用合成的析构函数还是定义自己的析构函数,都将是虚析构函数。只要基类的析构函数是虚函数,就能确保当我们delete基类指针时将运行正确的析构函数版本:
int main() { Quote *itemP = new Quote; //静态类型和动态类型一致 delete itemP; //调用Quote的析构函数 itemP = new Bulk_quote; //静态类型和动态类型不一致 delte itemP; //调用Bulk_quote的析构函数 }
注意:如果基类的析构函数不是虚函数,则delete一个纸箱派生类对象的基类指针将产生未定义的行为。
之前我们曾介绍过一条经验准则,即如果一个类需要析构函数,那么它同样需要拷贝和赋值操作。基类的析构函数并不遵循上述准则,它是一个重要的例外。一个基类总是需要析构函数,而且它能将析构函数设定为虚函数。此时,该析构函数为了成为虚函数而令内容为空,我们显然无法由此推断该基类还需要赋值运算符或拷贝构造函数。