1. 拷贝构造函数
如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
class Foo{
public:
Foo();//默认构造函数 (仅在程序员没有定义构造函数的情况下)
Foo(const Foo&);//拷贝构造函数
};
注:拷贝构造函数通常不应该是explicit的, 否则不能用于拷贝形式初始化
Sales_data item1(null_book); // 正确:直接初始化
Sales_data item2 = null_book; //错误:不能将explicit的构造函数用于拷贝形式初始化过程
2、合成拷贝构造函数synthesized copy constructor
如果我们没有给一个类定义拷贝构造函数,则编译器会定义一个拷贝构造函数。
2.1 定义
编译器将其参数的成员逐个拷贝到正在创建的对象中,编译器从给定对象中依次将每个非static成员拷贝到正在创建的对象中。
- 类类型成员:使用它的拷贝构造函数来拷贝
- 内置类型:直接拷贝
- 数组类型:逐元素拷贝
2.2 如果使用的初始化值要求通过一个 explicit 的构造函数来进行类型转换
vector<int> v1(10); //正确,直接初始化
vector<int> v2 = 10; //错误,接受大小参数的构造函数是explicit 的
void f(vector<int>); //f的参数进行拷贝初始化
f(10); //错误,不能使用一个explicit的构造函数拷贝一个实参
f(vector<int>10); //正确,从一个int值直接构造一个临时的vector
3、拷贝初始化
3.1 定义
. 使用指定构造函数创建一个临时对象
. 复制构造函数将那个临时对象复制到正在创建的对象。
3.2 两种初始化
直接初始化: 直接调用与实参匹配的构造函数
拷贝初始化:总是调用复制构造函数
string dots(10,'.'); //直接初始化
string s(dots); //直接初始化
string s2 = dots; //拷贝初始化
string s3 = "9-999-9"; //拷贝初始化
string s4 = string(100,'9'); //拷贝初始化
3.3 注意
. 如果一个类有一个移动构造函数,则拷贝初始化有时会使用移动构造函数而非拷贝构造函数。
. 当复制构造函数被声明为私有时,所有的复制初始化都不能使用。
3.4 拷贝初始化的其他情形:
不仅仅在使用= 定义变量时发生拷贝初始化,如下情况也会发生拷贝初始化:
- 将一个对象作为实参传递给一个非引用类型的形参。
- 从一个返回类型为非引用类型的函数返回对象。
- 用花括号列表初始化一个数组中的元素或者一个聚合类中的成员。
- 某些类类型还会对它们所分配的对象使用拷贝初始化,当初始化标准库容器时,调用其 insert 或 push 成员,容器会对其元素进行拷贝初始化,与之相对,用 emplace 创建的成员执行直接初始化。
4. 编译器可以绕过拷贝构造函数
在拷贝初始化的过程中,编译器可以(但不是必须)跳过拷贝/移动构造函数,直接创建对象。
eg.
string null_book = "9-999-9"; //拷贝初始化
->
string null_book("9-999-9"); //编译器略过了拷贝构造函数
- 前提条件:拷贝/移动构造函数必须是存在并且可访问的