嘿嘿,飙到第十三章了~~老司机的笑容像天上的弦月~~毕竟是补漏。。。

  1. 拷贝构造函数通常不应该是explicit的,参数必须为引用类型(常量引用)
  2. 合成的拷贝构造函数对于数组成员会逐一拷贝其元素
  3. 编译器有时会绕过拷贝构造函数(优化为直接构造)
  4. 三/五法则:析构函数,拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,可以看成一个整体,通常需要定义其中一个也就意味着需要定义其余四个。
  5. 新司机还记得=default不?
  6. 阻止拷贝:旧标准可以把相关构造函数声明为private,不需要定义;新标准可以用=delete。当不能拷贝、赋值或销毁类的成员时,类的合成拷贝控制成员就被定义为删除的
  7. 对于一个赋值运算符,编写时需要考虑:
    1. 一个对象赋值给它自身,要工作正常(所以要在析构当前对象前保存“新”对象)
    2. 拷贝+析构
  8. 如果swap操作不会拷贝对象而只是交换指针(通常都是如此),那么可以利用“拷贝并交换”技术来编写赋值运算:

    tc& operator=(tc rhs){  //传值复制了rhs
        swap(*this,rhs);    
        return *this;
    }   //rhs也就是原来的本对象会自动析构
    
  9. 左值引用不能绑定到要求转换的表达式、字面常量、返回右值的表达式,但是常量左值引用可以哦,而右值引用则和左值引用完成相反

    int i=10;
    double &rd=i;   //error
    int &ri=30;     //error
    int &rri=i+3;   //error
    
  10. 右值短暂,都是临时对象,左值持久。

  11. 右值和右值引用是不一样的,简单的说,右值就是临时量,不是变量,而右值引用是引用右值的变量,是个变量,它通过引用右值(临时量)将临时量变为变量,本来临时量马上就会消亡,你一用右值引用引用它,就把它变成一个持久的变量了,所以primer说,右值引用从绑定到其的临时量中“窃取”状态。同样的,右值引用是一个变量不是临时量,所以右值引用不能绑定右值引用(绕口。。。),还有,右值意味着它只有你这个所有者,你可以为所欲为(修改它)

  12. 使用std::move可以将一个左值转换为右值引用,也就是告诉编译器,这个左值劳资要当成右值用,你放心,过会儿劳资就削了它!(也就是说除了销魂不能对它再做任何操作,起始还可以再赋值给他,但是不能直接使用它的值。。。) 1. 移动构造函数和移动赋值运算符,参数都是非const的右值引用,调用后移后对象应处于可析构状态(意思是说,假设把a移动给b,就算你在调用移动构造函数之后把a析构了,也不会影响到b的内容,主要是考虑指针的处理),再者二者不应该抛出异常,使用noexcept关键字(编译器看到这个关键字的话,在这个函数抛出异常时,会直接调用terminate(),同时,它也告诉标准库容器该类的移动构造函数不会抛出异常,否则,标准库会因为预防发生异常的时候需要恢复原状而使用拷贝语义)。

  13. 拷贝左值,移动右值,没有移动就只能拷贝右值

  14. 标准库的重载函数往往有两个版本:

    void push_back(const X&);   //copy
    void push_back(X&&);    //move
    

    拷贝操作对参数应该没有影响,故而必然是const的,而移动操作会对移后对象进行操作,所以必然是非const的

  15. 可以在成员函数后面活着重载运算符后面加引用限定符表示this指向的对象只能是左值或者右值

    class foo{
    public:
        foo& operator=(const foo&) &;   //只有左值才能使用=
        void somefunc() &&;     //只有右值才能使用该函数
        void hehe() const &;    //只有const左值才能使用该函数,const必须在前
    }
    
  16. 至于重载函数,const限定符的成员函数和同名同参的普通成员函数构成重载,而不同引用限定符的同名同参成员函数之间也构成重载,重载的对象就是类本身是左值还是右值。另外,一旦在const限定符与普通重载函数之间的任何一个上用了引用限定符,那这一组重载函数中就都得用引用限定符了;