- 15.7 构造函数与拷贝控制
- 15.7.1 虚析构函数
- 15.7.2 合成拷贝控制与继承
- 15.7.3 派生类的拷贝控制成员
- 15.7.4 继承的构造函数
- 15.8 容器与继承(*)
- 15.9 文本查询程序再探(*)
一个基类总是需要析构函数,并且需要将其析构函数设置为虚函数。否则,delete 一个指向派生类对象的基类指针将产生为定义的行为。
如果一个类定义了析构函数,即使是声明了 = default,编译器也不会合成移动操作,那么其派生类就更没有了。
15.7.2 合成拷贝控制与继承 派生类合成的拷贝控制成员使用直接基类中对应的操作对一个对象的直接基类部分进行初始化、赋值或销毁的操作。
大多数基类都会定义一个虚析构函数,因此在默认情况下,基类通常不包含合成的移动操作,其派生类中也不会有。
一旦某个基类定义了自己的移动操作,也必须为其显示地定义拷贝操作。并且除非其派生类中含有排斥移动的成员,否则其派生类将自动获得合成的移动操作。
15.7.3 派生类的拷贝控制成员(1)定义派生类的拷贝或移动构造函数
通常使用对应的基类构造函数初始化对象的基类部分:
class Base { /* ... */ };
class D : public Base {
public:
// 默认情况下,基类的默认构造函数初始化对象的基类部分
// 要想使用拷贝或移动构造函数,我们必须在构造函数初始值列表中
// 显示地调用该构造函数
D(const D& d) : Base(d) // 拷贝基类成员
/* D 的成员的初始值 */ { /* ... */ }
D(D&& d) : Base(std::move(d)) // 移动基类成员
/* D 的成员的初始值 */ { /* ... */ }
};
如果没有提供基类的初始值的话,将会被默认初始化而不是基类部分对应初始化:
// 基类部分被默认初始化,而非拷贝
D(const D& d) /* 成员初始值,但是没有提供基类初始值 */
{ /* ... */ }
在默认情况下,基类默认构造函数初始化派生类对象的基类部分。如果我们想拷贝(或移动)基类部分,则必须在派生类的构造函数初始值列表中显式地使用基类的拷贝(或移动)构造函数。
(2)派生类赋值运算符
派生类的赋值运算符也必须显示地为其基类部分赋值:
// Base::operator=(const Base&) 不会被自动调用
D &D::operator=(const D &rhs)
{
Base::operator=(rhs); // 为基类部分赋值
// 按照过去的方式为派生类的成员赋值
// 酌情处理自赋值及释放已有资源等情况
return *this;
}
Base::operator= 将执行 Base 类的拷贝赋值运算符,至于其是由编译器合成的还是用户自己定义的无关紧要。
(3)派生类析构函数
派生类的析构函数只负责销毁由派生类自己分配的资源:
class D : public Base {
public:
// Base::~Base 被自动调用执行
~D() { /* 该处由用户定义清除派生类成员的操作 */ }
};
(4)在构造函数和析构函数中调用虚函数
如果构造函数或析构函数调用了某个虚函数,我们应该执行与构造函数或析构函数所属类型相对应的虚函数版本。
15.7.4 继承的构造函数 派生类不能继承默认、拷贝和移动构造函数,只能由编译器进行合成。
我们可以使用 using 声明来显式继承基类的构造函数:
class Bulk_quote : public Disc_quote {
public:
using Disc_quote::Disc_quote; // 继承 Disc_quote 的构造函数
double net_price(std::size_t) const;
}
使用 using 声明继承的构造函数形式如下:derived(parms) : base(args) {}
其中:
- derived:派生类名字
- base:基类名字
- parms:构造函数的形参列表
- args:派生类构造函数的形参传递给基类的构造函数
如果派生类有自己的数据成员,则这些数据成员将被默认初始化。
(1)继承的构造函数特点
- using 声明俺不会改变构造函数的访问级别,不论其声明在 private、public 还是 protected
- using 声明语句不能指定 explicit 或 constexpr。
- 若基类的构造函数含有默认实参,这些实参不会被继承。派生类会获得多个继承的构造函数,每个构造函数分别省略掉一个含有默认实参的实参。
- 如果派生类定义的构造函数与基类的构造函数具有相同的参数列表,则该构造函数不会被继承,而是用该构造函数替换本应继承的构造函数
- 默认、拷贝和移动构造函数不会被继承,只会被默认合成