《C++Primer》第十三章:拷贝控制

  1. 对象的拷贝、移动、赋值、销毁通过五种特殊的成员函数来控制。包括:拷贝构造函数、拷贝赋值函数、移动构造函数、移动赋值函数、析构函数。
  2. 当使用花括号列表的时候,会调用拷贝初始化,使用标准库容器的insert或者push成员的时候会调用拷贝初始化,非引用的参数传递与返回值需要拷贝初始化。需要注意的是,emplace创建的元素是直接初始化的。
  3. 拷贝构造函数的参数必须是引用类型,否则拷贝构造函数自身在传参的时候又要调用一次拷贝构造函数,就会一直调用下去。
  4. 有时候对一些对象使用=进行赋值但是没有重载相应的=运算符时也没有问题,这通常是由于进行了隐式类型转换并调用了相应的构造函数。使用explicit修饰构造函数阻止这种隐式调用。(explicit关键字只能出现在类内。)
  5. 上面提到的这种隐式类型转换,编译器只会自动执行一次类型转换步骤,而不能重复进行多次(P264)。
  6. 编译器可能自动的跳过拷贝构造函数,直接调用普通构造函数创建对象。
  7. 重载运算符本质上是函数,赋值运算符本质就是名为operator=的函数。合成拷贝运算符会将右侧每一个非static成员赋予左侧的相应成员。
  8. 析构函数执行与构造函数相反的操作,释放资源,销毁非static数据成员。不接收参数,因此没有重载。
  9. 隐式销毁一个内置指针类型的成员不会delete它所指向的对象,但是如果使用智能指针,由于是智能指针是类对象,那么在析构截断智能指针成员会被自动销毁。
  10. 合成析构函数默认为空。
  11. 五三原则:三个基本拷贝相关方法(拷贝构造函数、拷贝赋值运算符、析构函数)和两个新标准中的方法(移动构造函数、移动赋值运算符)。我们不需要定义所有这些操作,但是我们应该把这些操作看成一个整体。
  12. 如果需要自定义析构函数,那么往往也需要自定义拷贝构造函数(自定义析构函数往往由于指针,那么默认的拷贝构造函数会导致两个对象的数据成员指针指向一个地方)。
  13. 如果需要自定义拷贝操作,那么往往也需要自定义赋值运算符。反之亦然。
  14. =default使得这些操作为合成的版本,使用=delete使得上述这些操作被声明但是不能执行。
  15. 当编写赋值运算符的时候,要考虑到自己赋值给自己的这种特殊情况,通常赋值运算符会涉及到构造和析构各自的一部分工作,使用一个临时变量链接这两部分会更加鲁棒而清晰。
  16. 还应该重载swap方法,并声明为友元函数,很多基于交换的算法需要调用该方法。通常默认的swap已经足够了,会对于内置的各个数据成员调用swap,但是对于动态分配资源的类,定义swap是一种重要的优化性能的手段。
  17. 可以在赋值运算符中使用swap,这是一个小trick。此时的赋值运算符是异常安全的且可以处理自赋值的情况。
  18. 关于移动构造函数:如果使用allocator<string>.construct(p, 'asd'),这个是调用拷贝构造函数来构造p指向位置的字符串对象,而如果allocator<string>.construct(p, std::move(s)),则会使用标准库的移动构造函数,将s移动到p所在位置,此时内存不会被拷贝,而是接管内存的所有权。
本站总访问量