拷贝控制操作(copy control)
拷贝构造函数(copy constructor)、拷贝赋值运算符(copy-assignment operator)、移动构造函数(move constructor)、移动赋值运算符(move-assignment)、析构函数(destructor)。
拷贝构造函数
- 概念:如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
class A { public: A(const A&);//拷贝构造函数一般长这样 };
- 作用:将新对象初始化为同类型另一个对象的副本。
- 合成拷贝构造函数:如果我们从未提供拷贝构造函数,编译器会为我们合成一个,叫合成拷贝构造函数。合成拷贝构造函数会将其参数的非static成员逐个拷贝到正在创建的对象中。内置类型的成员直接拷贝,类类型成员使用其拷贝构造函数来拷贝。
显然合成拷贝构造函数能做的比较有限,如果有成员是指向堆区某块内存的指针,那么在拷贝对象的时候不能简单的把地址复制给新对象,而应该让新对象自己去堆区申请一块同样大小的内存并指向那块内存,然后把旧对象那块内存里的值复制给新对象那块内存。也就是常说的浅拷贝和深拷贝。
- 直接初始化和拷贝初始化:如果不使用等号初始化一个变量,执行的是直接初始化。如果使用等号,执行的是拷贝初始化。直接初始化是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。拷贝初始化依靠拷贝构造函数或移动构造函数来完成。
string dots(10,'.'); //直接初始化 string s(dots); //直接初始化 string s2 = dots; //拷贝初始化 string null_book = "9-999-99999-9"; //拷贝初始化 string nines = string(100, '9'); //拷贝初始化
- 拷贝初始化何时发生:(1)用等号定义变量;(2)将一个对象作为实参传递给一个非引用类型的形参;(3)从一个返回类型为非引用类型的函数返回一个对象;(4)用花括号列表初始化一个数组中的元素或一个聚合类中的成员;(5)初始化标准库容器或是调用其insert或push成员时。
- 编译器可以绕过拷贝构造函数:在拷贝初始化过程中,编译器可以(但不是必须)跳过拷贝/移动构造函数,直接创建对象。即,编译器允许将下面的代码string null_book = "9-999-99999-9";改写为string null_book("9-999-99999-9");。
- 练习13.5:给定下面的类框架,编写一个拷贝构造函数,拷贝所有成员。你的构造函数应该动态分配一个新的string,并将对象拷贝到ps指向的位置,而不是拷贝ps本身:
class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } private: std::string *ps; int i; };
class HasPtr { public: HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { } HasPtr(const HasPtr &orig) { ps = new std::string(*(orig.ps));//拷贝ps指向的对象,而不是拷贝指针本身 i = orig.i; } private: std::string *ps; int i; };
网友评论