拷贝构造函数
2023年12月29日 2023年12月31日
copy constructor
拷贝初始化时调用
只接受一个参数,是自身类类型的左值引用,或者其他所有参数都有默认值
之所以要求参数为左值引用: 此时不存在拷贝
拷贝源对象到目的对象
如果源对象是左值,匹配拷贝构造函数; 可能会发生一次隐式转换
建议不是explicit的, 允许隐式调用
拷贝与赋值
C++中的拷贝即拷贝初始化: 使用同类型或者兼容类型的另一个对象来初始化该对象
C++的赋值, 有一个前提: 该对象已被创建. 使用新的值替换旧值
拷贝构造函数的两种形式
-
形参为具有底层const的左值引用
1T(const T &);
通常使用这个版本, 具有底层const的左值引用可以用来绑定:
- 左值
最优
- 具有顶层const的左值
非最优
- 右值
非最优
- 左值
-
形参为左值引用
1T(T &);
较少使用此版本. 是拷贝构造函数. 只能接受允许执行写操作的左值
发生拷贝初始化的情形
- 定义对象时,使用赋值运算符给出初值
- 函数调用时,将对象作为实参传给非引用类型对象,或者返回一个非引用类型对象
- 使用insert和push为容器添加成员
拷贝构造函数的匹配优先级
1#include <iostream> 2 3using namespace std; 4 5class Foo 6{ 7public: 8 Foo(int aa) : a(aa) {} 9 Foo() : Foo(0) {} 10 Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &.\n"; } // 优先匹配 const Foo & 11 Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; } // 优先匹配 Foo & 12 13 void PrintFoo() { cout << a; } 14 15private: 16 int a; 17}; 18 19int main() 20{ 21 Foo f1(1); 22 const Foo f2(2); 23 Foo f3(f1); 24 Foo f4(f2); 25 26 return 0; 27}
输出
this is Foo &. this is const Foo &.
左值引用不能绑定具有顶层const的左值, 不能绑定右值
1#include <iostream> 2 3using namespace std; 4 5class Foo 6{ 7public: 8 Foo(int aa) : a(aa) {} 9 Foo() : Foo(0) {} 10 // Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &.\n"; } 11 Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; } 12 13 void PrintFoo() { cout << a; } 14 15private: 16 int a; 17}; 18 19int main() 20{ 21 Foo f1(1); 22 const Foo f2(2); 23 Foo f3(f1); 24 Foo f4(f2); // 错误 25 Foo f5(Foo(5)); // 错误 26 return 0; 27}
具有底层const的左值引用可以绑定左值和右值
1#include <iostream> 2 3using namespace std; 4 5class Foo 6{ 7public: 8 Foo(int aa) : a(aa) { cout << "called delegated constructor: a = " << a << ".\n"; } 9 Foo() : Foo(0) {} 10 Foo(const Foo &f) : Foo(f.a) { cout << "this is const Foo &: a = " << a << ".\n"; } 11 // Foo(Foo &f) : Foo(f.a) { cout << "this is Foo &.\n"; } 12 // Foo(const Foo &&) = delete; 13 void PrintFoo() { cout << a; } 14 15private: 16 int a; 17}; 18 19int main() 20{ 21 Foo f1(1); // 匹配Foo(int aa) 22 const Foo f2(2); // 匹配Foo(int aa) 23 Foo f3(f1); // 匹配Foo(const Foo &f) 24 Foo f4(f2); // 匹配Foo(const Foo &f) 25 Foo f5(Foo(5)); // 跳过拷贝构造函数, 匹配Foo(int aa) 26 Foo f6 = Foo(6); // 跳过拷贝构造函数, 匹配Foo(int aa) 27 Foo f7(std::move(Foo(7))); // 先匹配Foo(int aa), 再匹配Foo(const Foo &f) 28 return 0; 29} 30 31// 如果使用拷贝构造函数完成拷贝初始化: 在某些情况下, 不是只调用拷贝构造函数; 拷贝构造函数在最后一步调用
输出
called delegated constructor: a = 1. called delegated constructor: a = 2. called delegated constructor: a = 1. this is const Foo &: a = 1. called delegated constructor: a = 2. this is const Foo &: a = 2. called delegated constructor: a = 5. called delegated constructor: a = 6. called delegated constructor: a = 7. called delegated constructor: a = 7. this is const Foo &: a = 7.
拷贝构造函数的合成版本
synthesized copy constructor
如果没有给出拷贝构造函数的定义, 编译器会提供拷贝构造函数的合成版本
如果拷贝构造函数的合成版本不为删除 delete
, 其会将给定对象的每个非static数据成员拷贝到正在构造的对象中
数据成员类型 | 拷贝方式 |
---|---|
类类型 | 使用类的拷贝构造函数 |
内置类型 | 直接拷贝 |
内置数组 | 逐元素拷贝 |
每个数据成员的类型决定了它的拷贝方式
编译器可以略过拷贝/移动构造函数, 但要求其存在且可访问
- 不能定义为删除
delete
- 非私有成员