通过定义5种成员函数控制一个类的copy(copy control).
- copy constructor
- copy-assignment operator
- move constructor
- move-assignment operator
- destructor
Copy, Assignment and Destructor
- copy constructor
第一个参数是自身类型;所有额外参数都有默认值。
1 2 3 4 5 6
| class Foo { public: Foo(); Foo(const Foo&); }
|
合成拷贝构造函数
complier会至少给class合成一个copy constructor,无论是定义还是编译器优化后的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Sales_data { public: Sales_data(const Sales_data&); private: std::string bookNo; int units_sold = 0; double revenue = 0.0; }
Sales_data::Sales_data(const Sales_data &orig): bookNo(orig.bookNO), units_sold(orig.units_sold), revenus(orig.revenue) {}
std::string dots(10, "."); std::string s(dots);
std::string s2 = dots;
|
C++17后会尽量优化copy初始化,copy初始化性能较差,考虑隐性类型转换时使用,不支持explicit构造函数。
- 拷贝赋值运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class DefaultCopy { int x; std::string s; public: };
DefaultCopy a, b; a = b;
|
- 析构函数
对象销毁时调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class MyClass { private: int* data; public: MyClass(int size) { data = new int[size]; std::cout << "构造函数调用\n"; }
~MyClass() { delete[] data; std::cout << "析构函数调用\n"; } };
|
三五法则
拷贝构造函数、拷贝赋值运算符、析构函数
移动构造函数、移动赋值运算符
= default
显式要求编译器生成默认构造函数
1 2 3 4 5 6 7 8 9 10
| class MyClass { public: MyClass() = default; MyClass(const MyClass&) = default; MyClass(MyClass&&) = default; MyClass& operator=(const MyClass&) = default; MyClass& operator=(MyClass&&) = default; ~MyClass() = default; };
|
目的: 当提供了自定义的拷贝构造函数时,编译器不会自动生成默认的构造函数,如过需要的话,需要显式地指出。
- = delete
拷贝函数定义为= delete,显式声明不允许以这种方式进行拷贝
析构函数不可以定义为= delete,必须有销毁对象的方式
合成的拷贝控制成员函数也可能是删除的
- 类的某个成员的析构函数是删除或不可用的 -> 析构函数是删除的
- 类的某个成员的拷贝构造函数时删除或不可用的 -> 拷贝构造函数删除
- ————–拷贝赋值运算符————– -> 拷贝赋值运算符
- 析构函数删除或不可用,const或引用类型类成员没有类内初始化器 -> 默认构造函数是删除的
拷贝控制和资源管理
决定class是像一个值还是指针
像值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class HasPtr { public: HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) {} HasPtr(const HasPtr &p): ps(new std::string(*p.ps)), i(p.i) {} HasPtr& operator=(const HasPtr &); ~HasPtr() { delete ps; } private: std::string *ps; int i; };
HasPtr& HasPtr::operator=(const HasPtr &rhs) { auto newp = new string(*rhs.ps); delete ps; ps = newp; i = rhs.i; return *this; }
|
赋值运算符:1. 必须能正常工作;2. 组合了析构函数和拷贝构造函数的工作,避免内存泄漏
像指针
使用shared_ptr管理类资源,或者使用引用计数
引用计数工作方式
- 记录有多少对象与正在创建的对戏那个共享状态
- 拷贝构造函数不分配新的共享计数器,递增共享的计数器
- 析构函数递减计数器
- 拷贝赋值运算符递增右侧对象计数器,递减左侧对象计数器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class HasPtr { public: HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0), use(new std::size_t(1)) {} HasPtr(const HasPtr &p): ps(p.ps), i(p.i), use(p.use) { ++*use; } HasPtr& operator=(const HasPtr&); ~HasPtr(); private: std::string *ps; int i; std::size_t *use; }
HasPtr::~HasPtr() { if(--*use == 0) { delete ps; delete use; } }
HasPtr& HasPtr::operator=(const HasPtr &rhs) { ++*rhs.use; if(--*use == 0) { delete ps; delete use; } ps = rhs.ps; i = rhs.i; use = rhs.use; return *this; }
|
交换操作
内置数据使用std::swap(), 类使用内置swap()成员函数