Skip to content

Latest commit

 

History

History
164 lines (132 loc) · 5.78 KB

13拷贝控制.md

File metadata and controls

164 lines (132 loc) · 5.78 KB

cppp第十三章 阅读笔记

1.拷贝控制操作

  • 拷贝构造函数
  • 拷贝复制函数
  • 移动构造函数
  • 移动赋值函数
  • 析构函数

2.拷贝构造函数

一个构造函数的第一个参数是自身类型的引用,其余参数都有默认值,则它是一个拷贝构造函数。
  • 第一个参数可以不是const,但一般都是
  • 通常不应该是explicit的
  • 不会被隐没,即使有其他构造函数,编译器还是会帮你搓一个拷贝构造

3.直接初始化与拷贝初始化

string stra("6666");//直接初始化
string strb(stra);//直接初始化
string strc = stra;//拷贝初始化
直接初始化是在众多构造器中跑函数匹配
拷贝初始化是尝试转型成 T& ,然后丢给拷贝构造(或移动构造)
拷贝构造还发生在
* 非引用传参
* 非引用返回
* 花括号初始化(实际上还是非引用传参)

编译器可以将一个有效的拷贝初始化调用转换成对应的赋值初始化。

4.拷贝复制运算符

其实就是重载赋值符号,接受一个同类引用,返回自引用。

5.析构函数

其实是析构前函数,处理你自己产生的辣鸡

6.三/五法则

  • 要析构的类 大概率也要拷贝构造和拷贝赋值
  • 赋值和拷贝一般同要同不要

//upd:后文的意思是,想写一个你最好就写五个

7.default相关

显式的用系统合成的这堆函数可以写 = default;
若不想inline,则在类外 = default;

8.delete相关

class MyClass{
    MyClass(const MyClass&) = delete;
    //阻止拷贝
};
// c++11 真吉尔香
不过对于析构函数来讲
    如果析构函数被delete,那就不能定义这种类型的变量或创建临时对象。
    不过可以new一个动态的,但是不能delete掉。
    //上面的原理就是这个
    //不过这有什么应用吗?

那什么时候编译器会合成一个删除的拷贝控制函数给你呢?

  • 某成员的 析构 是删除或无法访问的,
    则合成析构为删除
  • 某成员的 拷贝构造或析构 是删除或无法访问的,
    则合成拷贝构造为删除
  • 某成员的拷贝赋值是删除或无法访问的,
    或类有const或引用成员,
    则合成拷贝赋值为删除
  • 某成员的 析构 是删除或无法访问的,
    或某引用成员没有类内初始化器,
    或某const成员没有类内初始化器且没有显式默认构造函数,
    则合成默认构造删除

你觉得它没有它就没有

所以现在有delete了,就别用私有化拷贝构造这种骚操作了

9.注意赋值运算符考虑自X

RT

10.手写swap

class MyClass{
    friend void swap(MyClass&,MyClass&);
};
inline void swap(MyClass &lmc, MyClass &rmc){
    ......
}

//所以我们以后用swap的时候要这么写
{
    using std::swap;
    swap(a,b);
}
//这样它用哪个就会自己选最好的了

11.神仙操作:拷贝并交换

//拷贝构造函数定义如下
MyClass& MyClass::operator=(MyClass mc){
    swap(*this,mc);
    return *this;
}
//由swap处理自X,scope结束析构处理原对象,行云流水

12.std::move()

将一个左值强制转化为右值引用
等他十八章讲?

13.右值引用

就是必须绑定到右值上的引用
这里非常强硬,你不能用decltype类似的骚操作把val加个括号就变成表达式(因为这个表达式依旧是个左值)
书上原话是
**右值引用只能绑定到一个即将销毁的对象上**
不过咱们还有static_cast 23333

//题外话是const左值引用也可以绑到右值表达式上,不过它毕竟是个const,所以根本就不叫绑,叫算完当场扔给他,不是动态结果
int val = 6666;
int &&rrefA = val * 10; // rrefA == val * 10
const int &ref = val * 10;// ref == 6666 * 10

int &&rrefB = rrefA;//error
//**右值引用只能绑定到一个即将销毁的对象上**

int &&rrefC = std::move(rrefA);//ok
// std::move强转右值引用 rrefC可以绑定,同样,我们应当认为rrefA里的东西已经被move走了,我们可以销毁或对它赋值,但是再读,读出啥就不一定了

14.移动构造和移动赋值

就是拷贝构造和拷贝赋值把左值引用换成右值引用

注意的点
    1.你拿完人家右值的东西,得帮人家擦干净,别跟个人渣似的。
    2.写上noexcept,否则别人会因为调用你可能出现异常而不用你,转用更安全的拷贝构造来保证不会因为移动到一半抛异常而全 部 木 大。

15.合成移动操作

当类没有定义任何自己版本的拷贝控制成员,且每个非静态成员都可以移动时,编译器会帮你搓这俩移动构造

如果不满足你还要编译器强搓一个( = default) 
那编译器会鲨了它( = delete)

还有,如果编译器帮你搓了移动两件套,那拷贝两件套就不会帮你搓了,会帮你定义成删除的。

16.移动迭代器

就是返回右值的迭代器
由make_move_iterator(iter)生成
可配合uninitialized_copy使用

17.成员函数与右值引用

以push_back为例,定义俩版本
1.接受一个常引用  收进来的赋值一份压进去 
2.接受一个右值引用  收进来的直接move进去

18.引用限定符

某成员函数想只作用于左值或右值的时候,我们可以打引用限定符
class MyClass{
public:
    MyClass& operator = (const MyClass&) &;
    //仅允许作用到左值引用
};
  • 限定对象是隐式this
  • 如果同时有const和&修饰,那么const在前。
  • 像const修饰符一样,可以重载函数
  • 如果有超过一个函数,他们区别仅在引用限定符或const,那他们必须都加引用限定符