C++11 中新增了 default
与 delete
这俩关键字。请不要误会,这里的 delete
并不是与 new
搭配使用释放内存的那个,而是跟 default
相对的用于类成员函数声明的关键字。
default
众所周知,如果用户自定义了一个类的构造函数,则编译器不会为其隐式生成默认构造函数。可能它觉得你想根据自己的需求定义构造函数。
class A {
public:
A(int i) {}
};
class B {};
class C {
public:
B(int i = 1) {}
};
A a; // ERROR! 已经有 A(int) 就不会生成 A()
B b; // OK! 隐式生成默认构造函数
C c; // OK! 所有形参都具有默认值
默认构造函数是在没有显式提供初始化式时调用的构造函数。它由不带参数的构造函数,或者为所有的形参提供默认实参的构造函数定义。如果定义某个类的变量时没有提供初始化时就会使用默认构造函数。(抄自百科)
默认构造函数并非空函数,所以不能直接用 {}
草草了事。其主要做的工作就是对成员变量进行一些默认初始化,这些工作的具体实现由编译器决定,若完全交给用户实现则较为困难。
如果我们想在上面的代码中使 A a
通过编译,那么此刻 default
关键字就派上用场了。只要在函数后面加上 = default
,则该函数会被视为预置(defaulted),编译器就会为其自动生成默认版本。
需要注意的是,default
只能用于六大特殊函数:默认构造、拷贝构造、拷贝赋值、移动构造、移动赋值、析构。毕竟用户自定义的函数不在"默认"范畴中。除此之外 default
既支持在类体内(in-line)声明,也支持体外(out-line)声明。
class A {
public:
A() = default; // in-line
A(int i) = default; // ERROR!
A(const A&);
};
// 尽管不加这句也会默认生成拷贝构造函数,但为了让"默认"表现地更直观
A::A(const A&) = default; // out-line
A a; // OK!
这样一来,就避免了程序员定义函数的繁重工作。同时,使用 default
修饰的函数也比用户自定义的函数拥有更高的执行效率,这也是该特性的一大优势。
delete
在 C++11 以前,如果想要禁止类的某个成员函数被调用,只有两种策略:要么将其声明为私有函数,要么干脆定义为空函数,什么也不做。因为有些类从功能的角度考虑必然不能对某些函数进行实现甚至定义——总不能让程序员在 human
类中搞了个 fly()
函数吧。
但问题在于,第一种做法并不能阻止类内或友元类的调用,和第二种做法一样无法在调用时报错,程序员也就很难找到代码问题所在。两种做法并没有从根本上解决问题——这些函数不该被定义。
于是,delete
被提出。与 default
相对,后面加上 = delete
的函数会被视为弃置(deleted),在编译器眼中这个函数禁止被定义,对该函数的调用会导致编译错误,继而从根本上解决了这个问题。与 default
不同,delete
可以用于修饰任何类成员函数。
class A {
public:
A() = default;
A(const A& other) = delete;
A& operator= (const A& other) = delete;
A(int i) = delete; // OK! 如果不想使用这种构造函数,就将其弃置,这样外部也无法进行定义
};
A a1, a3; // OK! 默认构造函数
A a2(a1); // ERROR! 拷贝构造函数被弃置
a3 = a1; // ERROR! 拷贝赋值运算符被弃置
一个很常用的例子就是
unique_ptr
,unique_lock
这些类通过delete
修饰来禁止对象之间的拷贝。