构造函数与拷贝控制
和其他类一样,位于继承体系中的类也需要控制当其对象执行一系列操作时发生什么样的行为,这些操作包括创建、拷贝、移动、赋值和销毁。
如果一个类(基类或派生类)没有定义拷贝控制操作,则编译器将为它合成一个版本。当然,这个合成的版本也可以定义成被删除的函数。
虚析构函数
继承关系对基类拷贝控制最直接的影响是基类通常应该定义一个虚析构函数,这样我们就能动态分配继承体系中的对象了。
如前所述,当我们delete一个动态分配的对象的指针时将执行析构函数。如果该指针指向继承体系中的某个类型,则有可能出现指针的静态类刑与被删除对象的动态类型不符的情况。
class Quote
{
};
class Bulk_quote:public Quote
{
};
例如,如果我们delete一个Quote*类型的指针,则该指针有可能实际指向了一个Bulk_quote类型的对象如果这样的话,编译器就必须清楚它应该执行的是Bulk guote的析构函数。和其他函数一样,我们通过在基类中将析构函数定义成虚函数以确保执行正确的析构函数版本,
class Quote {
public:
// 如果我们删除的是一个指向派生类对象的基类指针,则需要虚析构函数
virtual ~Quote() = default; //动态绑定析构函数
};
和其他虚函数一样,析构函数的虚属性也会被继承。因此,无论Quote的派生类使用合成的析构函数还是定义自己的析构函数,都将是虚析构函数。
只要基类的析构函数是虚函数,就能确保当我们delete基类指针时将运行正确的析构函数版本:
#include<iostream>
using namespace std;
class Quote
{
public :
virtual ~Quote()
{
cout << "基类析构" << endl;
}
};
class Bulk_quote :public Quote
{
~Bulk_quote()
{
cout << "派生类析构" << endl;
}
};
int main()
{
Quote* itemP = new Quote; // 静态类型与动态类型一致
delete itemP; //调用Quote的析构函数
itemP = new Bulk_quote; // 静态类型与动态类型不一致
delete itemP; // 调用Bulk_quote的析构函数
}
如果基类的析构函数不是虚函数,则delete一个指向派生类对象的基类指针将产生未定义的行为。
#include<iostream>
using namespace std;
class Quote
{
public :
~Quote()
{
cout << "基类析构" << endl;
}
};
class Bulk_quote :public Quote
{
~Bulk_quote()
{
cout << "派生类析构" << endl;
}
};
int main()
{
Quote* itemP = new Quote; // 静态类型与动态类型一致
delete itemP; //调用Quote的析构函数
itemP = new Bulk_quote; // 静态类型与动态类型不一致
delete itemP; // 调用Bulk_quote的析构函数
}
如果一个类需要析构函数,那么它也同样需要拷贝和赋值操作。
基类的析构函数并不遵循上述准则,它是个重要的例外。
一个基类总是需要析构函数,而且它能将析构函数设定为虚函数。
此时该析构函数为了成为虚函数而令内容为空,我们显然无法由此推断该基类还需要赋值运界符或拷贝构造函数。
虚析构函数将阻止合成移动操作
基类需要一个虚析构函数这一事实还会对基类和派生类的定义产生另外一个间接的影响: