读书笔记:C++ primer 5th edition--chapter13.拷贝控制


part1.拷贝、赋值与销毁
1.拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数对应的操作称为 拷贝控制操作
2.拷贝构造函数
1)第一个参数必须为引用类型。因为在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。否则构造函数调用无法成功。
2)由于常被隐式的使用,所以不能是explicit
3)合成拷贝构造函数,对类类型成员,会试用期拷贝构造函数来拷贝,内置类型则直接拷贝。也会逐元素的拷贝数组。
3.拷贝初始化与直接初始化
1)直接初始化要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。//emplace
2)拷贝初始化要求编译器讲右侧运算对象拷贝到正在创建的对象,并有类型转换如需。     //insert ,push
4.拷贝初始化除了=以外的场景
1)将对象作为实参传递给定非引用类型的形参或者返回类型为非引用类型
2)用花括号列表初始化数组元素
5.拷贝赋值运算符
1)赋值运算符通常返回一个指向其左侧运算对象的引用
2)合成拷贝赋值运算符会将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。此操作通过成员类型的拷贝赋值运算符来完成的。数组同样是逐元素赋值。
3)eg:
Sales_data&
Sales_data::operator=(const Sales_data &rhs){
     bookNo = rhs.bookNo;               //调用string::operator=
     unit_sold = rhs.units_sold;          //内置赋值
     return *this;                                  //返回对象的引用
}
6.析构函数
1)构造函数初始化非static数据成员,析构函数释放对象使用的资源,并销毁对象的非static数据成员。
2)不接受参数,无法被重载,给定类只有唯一一个析构函数
3)隐世地销毁一个内置指针类型的成员不会delete它所指向的对象。但是智能指针可成员在析构阶段会被自动销毁。
4)当指向一个对象的引用或指针离开作用域时,析构函数不会执行,需要手动delete。
5)合成析构函数体自身一般为空,并不直接销毁成员。成员是在析构函数提之后隐含的析构阶段中被销毁的,所以一共是两部分。
6)合成析构函数不会delete一个指针数据成员
7.三/五法则
1)需要析构函数的类也需要拷贝和赋值操作。因为上述的(6),对于指针成员,如果使用合成版本的拷贝和赋值运算符,这些函数简单拷贝指针成员,导致多个对象可能指向相同的内存。容易造成内存被释放多次。
2)需要拷贝操作的类也需要赋值操作,反之亦然。但二者都不意味着需要析构函数。
8.只能对具有合成版本的成员函数使用=default, 来要求编译器生成合成版本。类内用则使得函数成为内联。
9.c++11新增的阻止拷贝
1)删除函数:虽然定义了它们,但是不能以任何方式使用
2)通过添加=delete来通知编译器,必须出现在函数第一次声明的时候。且适用于任何函数。有助于引导函数匹配过程。
3)析构函数不能是删除的成员,否则无法被定义,因为会无法销毁对象。
4)如果一个类有数据成员不能默认构造、拷贝、复制或者销毁,则对应的成员函数将被定义为删除的。
5)对于具有引用成员或无法默认构造的const成员的类,编译器不会合成默认构造函数。
6)如果一个类有const成员,则它不能使用合成的拷贝赋值运算符。因为会复制所有成员,导致const对象被赋新值。
7)旧版本通过private来阻止拷贝,但不建议使用。

part2.拷贝控制和资源管理
1.类行为像值,有自己的状态,拷贝的副本与源对象独立。
1)如标准库容器和string类
2)需要类值拷贝赋值运算符
3)eg://异常安全
HasPtr& HasPtr::operator = (const HasPtr &rhs){
     auto newp = new string (*rhs.ps)
     delete ps;         //析构函数
     ps = newp;     //拷贝
     i = rhs.i;
     return *this;
}
4)大多数赋值运算符组合了析构函数和拷贝构造函数的工作
5)自赋值安全性很重要:好的方法就是在销毁左侧运算对象资源之前先拷贝右侧运算对象。
2.类型为像指针则共享状态。
1)如shared_ptr
2)也可以设计自己的引用计数,将计数器要保存在动态内存中,拷贝或赋值对象时再拷贝指向计数器的指针。
classe{
XXX
private:
     std::string *ps;
     int i;
     std::size_t *use
}
HasPtr(const HasPtr &p):
     ps.(p.ps), i(p.i), use(p.use)  { ++*use };
3)析构函数仅当use==0时释放内存。

part3.交换操作
1.定义自己的swap,使得交换指针而不是值。避免多余的拷贝与复制。
2.swap不是必须要的,但可能是一种优化手段。
3.赋值运算符中使用swap。叫做 拷贝并交换技术。会自动处理自赋值情况,且异常安全。
HasPtr& HasPtr::operator=(HasPtr rhs){
     swap(*this, rhs);
     return *this;
}
part4.拷贝控制示例。。跳过
part5.动态内存管理类,实现了简化的vector。。跳过
part6.对象移动
1.c++11最主要的特性是可以移动而非拷贝对象的能力。
2.用于对象拷贝后就立即被销毁的场景。比如vetor重新分配内存过程。
3.另一个原因是,io类或unique_ptr,都包含不能被共享的资源。不能拷贝但能移动。
4.右值引用
1)左值引用不能绑定到表达式、字面常量或返回优质的表达式。
2)相反,右值引用指向将要被销毁的对象。以窃取状态。
int &&rr = i*42;//right
int &&rr = i;//wrong
3)变量是左值,不能将右值引用绑定到一个变量上,即使变量是右值引用类型也不行。
5.移动构造函数,移动赋值运算符。。跳过 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值