一、默认函数和已删除函数的引入
C++的类中有四种特殊成员函数:默认构造函数、析构函数、拷贝构造函数及拷贝赋值运算符。
C++11 引入了移动语义,并将移动构造函数和移动赋值运算符添加到编译器可自动生成的特殊成员函数的列表中。
这对于简单类型非常方便,但是复杂类型通常自己定义一个或多个特殊成员函数,这可以阻止自动生成其他特殊成员函数。 具体规则:
如果显式声明了任何构造函数,则不会自动生成默认构造函数。
如果显式声明了虚拟析构函数,则不会自动生成默认析构函数。
如果显式声明了移动构造函数或移动赋值运算符,则:
不自动生成复制构造函数。
不自动生成复制赋值运算符。
如果显式声明了复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符或析构函数,则:
不自动生成移动构造函数。
不自动生成移动赋值运算符
【备注】
,C++11 标准指定将以下附加规则:
- 如果显式声明了复制构造函数或析构函数,则弃用复制赋值运算符的自动生成。
- 如果显式声明了复制赋值运算符或析构函数,则弃用复制构造函数的自动生成。
在这两种情况下,Visual Studio 将继续隐式自动生成所需的函数且不发出警告。
这些规则的结果也可能泄漏到对象层次结构中。 例如,如果出于任何原因,基类无法具有可从派生类调用的默认构造函数(即 public
protected
不带任何参数的或构造函数),则从派生的类不能自动生成其自己的默认构造函数。
class Test
{
private:
int a;
};
int main()
{
Test t;//OK,编译器隐式地生成了默认构造函数
Test t2(t);//0K,编译器隐式地生成了拷贝构造函数
Test t3=t;//0K,编译器隐式地生成了拷贝赋值运算符(重载)
//程序结束,编译器隐式地生成了析构函数进行对象销毁
}
如果类设计者又实现了这些函数的自定义版本后,编译器就不会去生成默认版本。
class Test
{
private:
int a;
public:
Test(int aa):a(aa) {}
//Test(const Test& t) { a = t.a; }
};
int main()
{
Test t;//error,编译器不会隐式地生成了默认构造函数,因为重写了构造函数
Test t2(2);//0K,调用了定义的Test(int)构造函数
}
C++11标准引入了新特性:类默认函数的控制:=default (默认函数)和 =delete (已删除函数)
默认函数和已删除函数使得可以显式控制是否自动生成特殊成员函数。 已删除的函数还可以提供简单语言,以防止所有类型的函数(特殊成员函数和普通成员函数以及非成员函数)的自变量中出现有问题的类型提升,这会导致意外的函数调用。
二、默认函数
可以默认设置任何特殊成员函数——以显式声明特殊成员函数使用默认实现 ,定义非public的特殊成员函数、恢复其他情况下被阻止自动生成的特殊成员函数。(在声明特殊成员函数时,末尾添加“=default”)
class Test
{
private:
int a;
public:
Test()=default;//显式声明使用默认构造函数
Test(int aa):a(aa) {}
//Test(const Test& t) { a = t.a; }
};
int main()
{
Test t;//OK,使用了默认函数
Test t2(2);//0K,调用了自定义的Test(int)构造函数
}
由于普通特殊成员函数的性能优势,因此建议在需要默认行为时首选自动生成的特殊成员函数而不是空函数体。可以通过显式默认设置特殊成员函数,或不声明它(不阻止自动生成特殊成员函数)来实现此目的。
三、已删除函数
可以删除特殊成员函数以及普通成员函数和非成员函数,以阻止定义或调用它们。 删除特殊成员函数提供了一种使编译器无法生成不需要的特殊成员函数的更清晰的方式。 必须在声明函数时将其删除;不能在这之后通过声明一个函数然后不再使用的方式来将其删除。
①删除特殊成员函数
class Test
{
private:
int a;
public:
Test() = default;
Test(const Test& t) = delete;
Test& operator=(const Test& t) = delete;
};
int main()
{
Test t;//OK,使用了默认函数
Test t2(2);//error,禁用拷贝构造函数
Test t3;//OK,使用了默认函数
t3 = t2;//error,禁用拷贝赋值运算符
}
②删除普通成员函数或非成员函数可阻止有问题的类型提升导致调用意外函数。
void onlyDouble(int)=delete;
void onlyDouble(double)
{
return;
}
int main()
{
double a = 3.14;
onlyDouble(a);//Ok,参数类型匹配成功
int b = 3;
onlyDouble(b);//error,禁用int参数自动提升为double,并调用double版本函数
}