虚析构函数
虚析构函数由于动态绑定可以确保delete基类指针时将运行正确的析构函数版本。一般,如果一个类需要析构函数,那么它同样需要拷贝和赋值操作。但基类的析构函数不遵循上述准则,是一个重要的例外。
虚析构函数将组织合成移动操作。如果一个类定义了析构函数,即使它通过=default的形式使用了合成版本,编译器也不会为这个类合成移动操作。
(如果我们不声明自己的拷贝构造函数或拷贝赋值运算符,编译器总会为我们合成这些操作。拷贝操作要么被定义成逐成员拷贝,要么被定义为对象赋值,要么定义为delete的函数。)
合成拷贝控制与继承
基类或派生类可能出于某些原因将合成的默认构造函数或者任何一个拷贝控制成员定义为delete函数。此外,某些定义基类的方式也可能导致有的派生类成员称为delete的函数:
- 如果基类中的默认构造函数、拷贝构造函数、拷贝赋值运算符或析构函数是delete的或者不可访问的(例如private),则派生类中对于的成员将是delete的,原因是编译器不能使用基类成员来执行派生类对象基类部分的构造、赋值或销毁操作
- 如果在基类中有一个不可访问或删除的析构函数,则派生类中合成的默认和拷贝构造函数将是delete,因为编译器无法销毁派生类对象的基类部分
- 编译器将不会合成一个delete的移动操作。当我们使用=default请求一个移动操作时,如果基类中的对应操作是删除的或不可访问的,那么派生类中该函数将是delete,原因是派生类对象的基类部分不可移动。
派生类的拷贝控制成员
当派生类定义了拷贝或移动操作时,该操作负责拷贝或移动包括基类部分在内的整个对象
class Base {/*……*/}
class D : public Base {
public:
//默认情况下,基类的默认构造函数初始化对象的基类部分
//想要使用拷贝或移动构造函数,我们必须在构造函数初始化列表中
//显式地调用该构造函数
D(const D& d) : Base(d)
/*D的成员初始值*/ {/*……*/}
D(D&& d) : Base(std::move(d))
/*D的成员初始值*/ {/*……*/}
};
在默认情况下,基类默认构造函数初始化派生类对象的基类部分。如果我们想拷贝(移动)基类部分,则必须在派生类的构造函数初始化列表中显示地使用基类的拷贝(移动)构造函数
派生类赋值运算符
D &D::operator=(const D &rhs)
{
Base::operator=(rhs);
/*……*/
return *this;
}
如果构造函数或析构函数调用了某个虚函数,则我们应该指向与构造函数或析构函数所属类型相对于的虚函数版本。
继承的构造函数
一个类只初始化它的直接基类,也只继承其直接基类的构造函数。类不能继承默认、拷贝和移动构造函数。如果派生类没有直接定义这些构造函数,编译器将为派生类合成他们。派生类继承基类构造函数的方式是提供一条注明了直接基类名的using声明语句:
class Bulk_quote : public Disc_quote{
public:
using Disc_quote::Disc_quote;
double net_price(std::size_t) const override;
};
通常情况下,using声明语句只是令某个名字在当前作用域内可见,而当作用于构造函数时,using声明语句将令编译器产生代码,即对于基类的每个构造函数,编译器都在派生类中生成一个形参列表完全相同的构造函数:
derived(parms) : base(args) {}
Bulk_quote(const std::string& book, double p,
std::size_t qty, double disc) :
Disc_quote(book, p, qty, disc) {}
继承的构造函数的特点
和普通成员的using声明不同,一个构造函数的using声明不会改变该构造函数的访问级别(不管using出现在哪,基类的private构造函数在派生类中还是private)。而且,一个using声明语句不能指定explicit和constexpr(如果基类的构造函数是explicit或者constexpr,继承的构造函数也具有相同的属性)。
当一个基类构造函数含有默认实参时,这些实参不会被继承。且派生类将或得多个继承的构造函数,其中每个构造安徽省农户分别省略掉一个含有默认实参的形参。(即,如果基类有一个接受两个形参的构造函数,其中第二个形参含有默认实参,则派生类将获得两个构造函数:1.构造函数接受两个形参(没有默认实参);2.构造函数只接收一个形参,对应基类中最左侧的没有默认实参的那个形参)
如果基类含有几个构造函数,则除了两个列外情况,大多数时候派生类会继承所有这些构造函数:
- 派生类可以继承一部分构造函数,而为其他构造函数定义自己的版本。如果派生类定义的构造函数与基类的构造函数具有相同的参数列表,则该构造函数不会被继承,定义在派生类中的构造函数将替换继承而来的构造函数
- 默认、拷贝和移动构造函数不会被继承