第十三章----拷贝控制
1、拷贝控制操作这一节主要讲了包括拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符和析构函数。
2、拷贝构造函数:如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,此构造函数是拷贝构造函数。
public:
Foo(); //默认构造函数
Foo(const Foo&); //拷贝构造函数
};
拷贝构造函数的第一个参数是必须是自身类类型的引用类型,如果不是引用,是传值或其他的话会无限递归。
3、合成拷贝构造函数:我们没有为类定义拷贝构造函数,编译器会帮我们定义一个。编译器从给定对象中依次将每个非static成员(静态成员是不被任何对象包含的,)拷贝到正在创建的对象中。
4、拷贝初始化
拷贝初始化不仅在=定义变量时发生,还会在以下几种情况:
将一个对象作为实参传递给一个非引用类型的形参;
从一个返回类型为非引用类型的函数返回一个对象;
用花括号列表初始化一个数组中的元素或一个聚合类中的成员。
只有一个类没有定义任何拷贝控制成员(拷贝构造函数,拷贝赋值运算符,析构函数)时, 且类的所有非static成员都是可移动的, 此时编译器才会给该类合成移动构造函数和移动赋值运算符.
5、拷贝赋值运算符
Sales_data trans,accum;
trans=accum; //使用Sales_data的拷贝赋值运算符
和拷贝构造函数一样,如果类未定义拷贝赋值运算符,编译器会合成一个。赋值构造函数必须注意它的函数原型,参数通常是引用类型,返回值通常也是引用类型,否则在传参和返回的时候都会再次调用一次拷贝构造函数。
6、析构函数
析构函数执行的与构造函数执行的顺序相反,析构函数释放对象所使用的资源,并销毁对象的非static数据成员(静态成员生存期是整个程序)。析构函数不接受参数,因为没有参数个数和类型,所以不能被重载,对于一个类只有唯一一个析构函数。隐式销毁一个内置指针类型的成员不会delete它所指向的对象。
在以下几种情况下会调用析构函数:
a.变量在离开其作用域的时候;
b.当一个对象被销毁时,其成员被销毁;
c.容器被销毁时;
d.当对指向它的指针应用delete运算符时被销毁;
e.对于临时对象,当创建它的完整表达式结束时被销毁。
7、三/五法则
需要析构函数的类也需要拷贝和赋值操作,反之不然。需要拷贝操作的类也需要赋值操作,反之亦然。
8、 使用=default
我们可以通过将拷贝控制成员定义为=default来显示地让编译器来为我们生成默认版本。
9、 阻止拷贝:加上=delete
新标准下,我们可以通过将拷贝构造函数和赋值运算符定义为删除的函数来阻止拷贝和赋值。=delete必须出现在函数第一次声明的时候。析构函数不能是删除的成员。删除一个类的析构函数或者类的某个成员的类型删除了析构函数,我们都不能定义该类的变量或临时对象。但可以动态分配这种类型,不能释放。C++11之前,通过将类的拷贝构造函数和拷贝赋值运算符声明为private的且不定义来阻止对该类对象的拷贝(或拷贝初始化).就算是友元也不能拷贝该类型, 因为未定义。
10、一条原则:移动右值,拷贝左值,左值持久,右值短暂.(右值是内容,左值是身份,是内存位置),所以右值移动比拷贝更效率,左值是内存,不能移动,只能拷贝了。地址当然是持久的,内容可以改变。
11、变量是左值, 所以不能将一个右值引用绑定到变量上. 就算这个变量本身就是一个右值引用也不行,移动构造函数的本质其实就是夺取一个对象的内存资源直接给被构造的对象,且原来对象不能继续控制本来的内存了.