0%

C++11新特性 - 编译器自动生成函数

本文主要介绍 C++11 之后编译器自动为类生成的函数,以及新的生成规则

编译器自动为类生成的函数

在 C++ 中,如果我们定义了一个空类

1
class Dog {};

编译器会自动为这个类生成构造函数、析构函数等。具体来说,我们定义了以上的一个 Dog 类,编译器会将它补全成一个如下自带6个函数的类:

1
2
3
4
5
6
7
8
9
10
11
class Dog {
// C++03
Dog(); // 默认构造函数
Dog(const Dog&); // 拷贝构造函数
Dog& operator=(const Dog&); // 拷贝赋值运算符
~Dog() // 析构函数

// C++11 新增
Dog(Dog&&); // 移动构造函数
Dog& operator=(Dog&&); // 移动赋值运算符
};

自动生成函数规则

然而,并不是所有情况下,编译器都会生成所有6个函数 —— 假如用户定义了某种类型的构造函数,编译器则不会生成某种其他类型构造函数。自动函数生成遵循以下规则:

  1. 默认构造函数:只在用户没有定义其它类型构造函数时才会生成
  2. 拷贝构造函数:只有用户没有定义 移动构造函数(5) 和 移动赋值运算符(6) 时,才会生成
  3. 拷贝赋值运算符:只有用户没有定义 移动构造函数(5) 和 移动赋值运算符(6) 时,才会生成
  4. 析构函数:无限制
  5. 移动构造函数:只有用户没有定义 拷贝构造函数(2), 拷贝赋值运算符(3), 析构函数(4) 和 移动赋值运算符(6) 时,才会生成
  6. 移动赋值运算符:只有用户没有定义 拷贝构造函数(2), 拷贝赋值运算符(3), 析构函数(4) 和 移动构造函数(5) 时,才会生成

例如,如果我们定义一个带有拷贝构造函数的类

1
2
3
4
5
6
7
class Cat {
Cat(const Cat&); // 拷贝构造函数

// 自动生成拷贝赋值运算符和析构函数
};

Cat cat; // 报错,Cat 类无默认构造函数

另一个例子:

1
2
3
4
5
class Duck {
Duck(Duck&&); // 移动构造函数

// 仅自动生成析构函数
};

在实践中,这个特性非常有用 —— 有些资源只应该被移动而不应该被拷贝,mutexsocket

构造函数带默认值的例子:

1
2
3
4
5
class Frog {
Frog(Frog &&, int = 0); // 移动构造函数

// 仅自动生成析构函数
};

在这个例子中当第二个函数不存在时,Frog 是通过 Frog 右值构造的,因此 Frog(Frog &&, int = 0) 函数是移动构造函数,同理有:

1
2
3
4
5
class Frog {
Frog(Frog &&, int = 0); // 移动构造函数
Frog(int = 0); // 默认构造函数
Frog(const Frog&, int = 0); // 拷贝构造函数
};