C++Primer 第十三章 拷贝控制

//1.当定义一个类时,我们显示地或隐式地指出在此类型的对象(注意这里是此类型的对象,而不包括此类型的指针)拷贝,移动,赋值,销毁时做什么。一个类通过定义五种特殊的成员函数来控制这些操作:拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数。
//当定义了五种特殊成员函数的其中一个的时候,一般也需要定义其他几个操作。
//拷贝构造函数的第一个参数必须是一个引用类型,若第一个参数不是引用类型则会造成逻辑错误:传值调用会产生一个临时对象,而此对象必须通过类的拷贝构造函数生成,所以会陷入死循环

//2.类的转换构造函数会自动调用。
CMyString(char *temStr) : str(temStr){printf("b0");}
CMyString str0 = "s0";    //这里将调用类的转换构造函数

//3.类的赋值运算符应该返回一个指向其左侧运算符对象的引用。返回左侧对象是为了能连续赋值,返回引用是为了提高效率。

//4.在一个析构函数中,首先执行函数体,然后按照成员初始化顺序逆序销毁成员。当在一个容器销毁其元素时,容器中的元素会自动执行其析构函数。
//  当指向一个对象的引用或指针离开作用域的时候,其指向类型的析构函数不会被调用。当一个动态分配的类类型指针,其被delete的时候,其指向类型的析构函数会被调用。
//  当一个类需要定义析构函数的时候,一般来说也需要定义其他的拷贝操作。
//  当一个类的析构函数被定义为不可访问的时候,不允许定义此类的对象。可以动态分配这种类型的对象,但是不能释放它。

//5.合成的拷贝控制成员可能是删除的:
A:如果类的某个成员的析构函数是删除或者不可访问的,则类的合成析构函数和类的合成拷贝构造函数被定义为删除的。
B:如果类的某个成员的拷贝构造函数是删除或不可访问的,则类的合成拷贝构造函数被定义为删除的。
C:如果类的某个成员的拷贝赋值运算符是删除或不可访问的,或是类有一个const或引用成员,则类的合成拷贝赋值运算符被定义为删除的。
D:如果类的某个成员的析构函数是删除或是不可访问的,或类有一个引用成员并且没有类内初始化器,或者类有一个const成员没有类内初始化器且类型没有指定默认构造函数,则该类的默认构造函数被定义为删除的。
总结:如果类的数据成员不能默认构造,拷贝,复制,销毁,则对应的成员函数将被定义为删除的。

//6.在private中声明类的拷贝构造函数等系列函数,并且不定义此类型函数,可以防止此类型对象被拷贝。注意点:不能定义,否则类的成员函数或者类的友员仍然能访问这些函数。

//7.赋值运算符通常组合了析构函数和拷贝构造函数的操作。注意点:如果将对象赋予其本身,赋值运算符必须能正常工作。

//8.通过&&来获得右值引用,右值引用有一个特殊性质:只能绑定到即将销毁的对象上(不能将右值引用直接绑定在左值上),来实现资源的移动。
//  左值持久,右值短暂。由于右值引用变量本身是一个左值变量,所以不能将右值引用绑定到右值引用对象上。
//  可以通过std::move函数来获得绑定到左值上的右值引用。此函数定义在头文件utility。声明在命名空间std中。
//  move调用告诉编译器:我们有一个左值,但是我们希望像一个右值一样处理它。在调用move后就意味着承诺:对被调用变量,除了赋值或销毁外,我们将不再使用它。在调用std::move后,对被调用变量的值不能做任何假设。
//  使用move的时候,用如下方法:std::move。这样可以防止潜在的命名冲突。
//  当我们编写一个移动操作的时候,从一个对象移动资源而并不会销毁此对象,所以必须确保移后源对象进入一个有效的且可析构状态。
//  由于移后源对象具有不确定状态,所以当我们调用move后,必须绝对确认移后源对象没有其他用户。在代码中小心使用move可以极大的提高性能。

//9.当没有定义自己的移动操作的时候,通过函数匹配,会调用对应的拷贝操作版本。
//  只有当一个类没有定义任何自己版本的拷贝控制成员的时候,且类的每个非static成员都可以移动的时候,编译器才会定义移动控制成员。
//  当类定义了自己的移动操作后,会将其对应的默认拷贝操作定义为删除的。

//10.make_move_iterator:将普通迭代器转为移动迭代器。make_move_iterator定义在头文件iterator中。声明在命名空间std中。
allocator<unique_ptr<char>> allocStr;
auto pStr = allocStr.allocate(10);
vector<unique_ptr<char>> vecStr(10);
generate(vecStr.begin(), vecStr.end(), [](){return (unique_ptr<char>)new char(0);});
//uninitialized_copy(vecInt.begin(), vecInt.end(), pStr);                                        //此句代码非法。unique_ptr对象是不可拷贝的
uninitialized_copy(make_move_iterator(vecStr.begin()), make_move_iterator(vecStr.end()), pStr);    //正确执行,将vecStr的资源移动到了pStr所指向的内存中。

//11.如果一个成员函数同时提供拷贝和移动版本,也能提高性能。
//   void push_back(_Ty&& _Val)          :接受右值引用版本,来移动资源。对于存储unique_ptr类型的元素的容器的push_back操作就是调用此版本。
//   void push_back(const _Ty& _Val)     :此版本是拷贝版本。
原文地址:https://www.cnblogs.com/szn409/p/5597739.html