当定义一个类时,我们需要显示或隐式地指定此类型对象的拷贝,赋值,移动,销毁 操作
拷贝构造函数(当用本类的类对象来初始化另一个本类对象时)
- 声明:Foo(const& Foo);
- 拷贝初始化与直接初始化:
string dots(10,“ ”); //直接初始化
string s(dots); //直接初始化
string s2=dots; //拷贝初始化
string null_book="999"; //拷贝初始化
直接初始化:要求编译器使用与参数匹配的构造函数
拷贝初始化:拷贝初始化总是调用拷贝构造函数。拷贝初始化首先使用指定构造函数创建一个临时对象,
然后用拷贝构造函数将那个临时对象拷贝到正在创建的对象;
拷贝初始化不仅在使用=的时候:
1.将一个对象作为实参传递给一个非引用类型的形参
2.从一个返回类型为非引用类型的函数返回一个对象
3.用花括号列表初始化一个数组中的元素或一个聚合类的成员
拷贝赋值运算符
- 声明:Foo& operator=(const Foo&);
- 若在类内未显式定义,则编译器会自动生成合成拷贝赋值运算符,它主要是将运算符右侧的所有非static成员赋给左侧元算对象对应成员(或是用来禁止该类型对象的赋值)
移动构造函数
- 声明:Foo(Foo&&) noexcept;
- 移动构造函数从给定对象窃取而不是拷贝资源,会减少程序的开销
- 除了完成资源移动,移动构造函数还必须保证移后源对象处于这样一种状态——销毁它是无害的
- 移动构造函数不分配新的内存,移动操作不会抛出任何异常,因此在构造函数中指明noexcept;
- 移动构造函数抛出异常时,旧空间的移后源对象已经被改变,而新空间未构造元素可能尚不存在,因此要指明noexcept;
移动赋值运算符
- 声明:Foo& operator=(Foo&&) noexcept;
- 当一个类为其自身定义了拷贝构造函数和拷贝赋值运算符或者析构函数,且其所有数据成员皆可移动构造时,那么编译器将不会再为该类合成移动构造函数和移动赋值运算符
析构函数
- 声明:~Foo();
- 析构函数销毁对象使用的资源,并释放对象得到非static成员,析构函数析构的部分是隐式的
- 成员的销毁完全依赖于其本身的类型,类类型需要执行自身的析构函数,而内置类型则什么也不做(无析构函数)
- 调用情况:
1:变量离开作用域时被销毁
2:当对象被销毁,其成员被销毁
3:容器被销毁,成员被销毁
4:动态分配的对象,指针被delete时
5:临时对象,创建的完整表达式结束时
三五法则
-
需要析构函数的类也需要拷贝构造函数和拷贝赋值函数
-
需要拷贝操作的类也需要赋值操作,反之亦然
-
析构函数是不能删除的
-
如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的
-
如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作
=default与=delete
- 对具有合成版本的成员定义为=default来显示要求编译器生成合成版本
- 合成的函数将隐式地声明为内联函数,如果不希望是内联,就只对类外定义使用=default
- 可以对任何类内函数(析构函数除外)声明 =delete ,且必须出现在函数第一次声明的时候,如果析构函数被声明=delete ,析构函数被删除,就无法销毁此类型的对象
- 本质上,当不可能拷贝、赋值、销毁类的成员时,类的拷贝构造函数会被定义为删除的
拷贝控制与资源管理
- 通常管理类外资源的类必须定义拷贝控制成员,如果一个类需要析构函数来释放资源,那么该类也需要拷贝构造函数和拷贝赋值运算
- 可以用两种方式定义拷贝控制:
1.类的行为像一个值,拷贝时,副本与原对象相互独立
2.类的行为像指针的共享状态,副本与原对象有相同的底层数据
右值与右值引用
- 右值引用:必须绑定到右值的引用通过两个取址符号&&来获得右值引用:只绑定到一个将要销毁的对象,因此,我们可以自由的将右值引用的资源“移动”到另一个对象中
- 左值:lvalue,在内存中占有确定位置的变量
- 右值:rvalue,字面值常量或在表达式求值过程中临时创建的对象
- 左值持久,右值短暂
- 右值引用(c++11标准):绑定到右值的引用(&&),只能绑定到一个将要被销毁的对象,因为没有其他用户进行绑定且要被销毁,所以使用右值引用的代码可以自由的接管所引用的对象的资源
右值引用与成员函数
如果一个成员函数同时提供拷贝和移动版本,它也能从其中受益
- void push_back(const x& ) //拷贝版
- void push_back(x&&) //移动版本
左值与右值成员函数
- Foo sorted() const & 左值
- Foo sorted() && 右值
- 当我们定义两个或两个以上的具有同名且参数列表相同的成员函数,必须对所有函数都加上引用限定符或者都不加