详解在详解在C++中显式默认设置的函数和已删除的函数的方法中显式默认设置的函数和已删除的函数的方法
在 C++11 中,默认函数和已删除函数使你可以显式控制是否自动生成特殊成员函数。已删除的函数还可为您提供简单语言,
以防止所有类型的函数(特殊成员函数和普通成员函数以及非成员函数)的参数中出现有问题的类型提升,这会导致意外的函
数调用。
显式默认设置的函数和已删除函数的好处
在 C++ 中,如果某个类型未声明它本身,则编译器将自动为该类型生成默认构造函数、复制构造函数、复制赋值运算符和析
构函数。这些函数称为特殊成员函数,它们使 C++ 中的简单用户定义类型的行为如同 C 中的结构。也就是说,可以创建、复
制和销毁它们而无需任何额外编码工作。C++11 会将移动语义引入语言中,并将移动构造函数和移动赋值运算符添加到编译
器可自动生成的特殊成员函数的列表中。
这对于简单类型非常方便,但是复杂类型通常自己定义一个或多个特殊成员函数,这可以阻止自动生成其他特殊成员函数。实
践操作:
如果显式声明了任何构造函数,则不会自动生成默认构造函数。
如果显式声明了虚拟析构函数,则不会自动生成默认析构函数。
如果显式声明了移动构造函数或移动赋值运算符,则:
不自动生成复制构造函数。
不自动生成复制赋值运算符。
如果显式声明了复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符或析构函数,则:
不自动生成移动构造函数。
不自动生成移动赋值运算符。
注意注意
此外,C++11 标准指定将以下附加规则:
如果显式声明了复制构造函数或析构函数,则弃用复制赋值运算符的自动生成。
如果显式声明了复制赋值运算符或析构函数,则弃用复制构造函数的自动生成。
在这两种情况下,Visual Studio 将继续隐式自动生成所需的函数且不发出警告。
这些规则的结果也可能泄漏到对象层次结构中。例如,如果基类因某种原因无法拥有可从派生类调用的默认构造函数 – 也就
是说,一个不采用任何参数的 public 或 protected 构造函数 – 那么从基类派生的类将无法自动生成它自己的默认构造函数。
这些规则可能会使本应直接的内容、用户定义类型和常见 C++ 惯例的实现变得复杂 — 例如,通过以私有方式复制构造函数和
复制赋值运算符,而不定义它们,使用户定义类型不可复制。
struct noncopyable
{
noncopyable() {};
private:
noncopyable(const noncopyable&);
noncopyable& operator=(const noncopyable&);
};
在 C++11 之前,此代码段是不可复制的类型的惯例形式。但是,它具有几个问题:
复制构造函数必须以私有方式进行声明以隐藏它,但因为它进行了完全声明,所以会阻止自动生成默认构造函数。如果你需要
默认构造函数,则必须显式定义一个(即使它不执行任何操作)。
即使显式定义的默认构造函数不执行任何操作,编译器也会将它视为重要内容。其效率低于自动生成的默认构造函数,并且会
阻止 noncopyable 成为真正的 POD 类型。
尽管复制构造函数和复制赋值运算符在外部代码中是隐藏的,但成员函数和 noncopyable 的友元仍可以看见并调用它们。如
果它们进行了声明但是未定义,则调用它们会导致链接器错误。
虽然这是广为接受的惯例,但是除非你了解用于自动生成特殊成员函数的所有规则,否则意图不明确。
在 C++11 中,不可复制的习语可通过更直接的方法实现。
struct noncopyable
{
noncopyable() =default;
noncopyable(const noncopyable&) =delete;
noncopyable& operator=(const noncopyable&) =delete;
};
请注意如何解决与 C++11 之前的惯例有关的问题:
仍可通过声明复制构造函数来阻止生成默认构造函数,但可通过将其显式设置为默认值进行恢复。
显式设置的默认特殊成员函数仍被视为不重要的,因此性能不会下降,并且不会阻止 noncopyable 成为真正的 POD 类型。
复制构造函数和复制赋值运算符是公共的,但是已删除。定义或调用已删除函数是编译时错误。
评论0