这里所说的复制控制包括,复制构造函数、赋值操作符、析构函数。
- 首先,有一点要说明的(《Effective C++中文版》中提到)。当我们定义一个空类的时候,其实编译器会给我们做很多的事情,编译器会给我们声明一个copy构造函数、一个copy assignment操作符和一个析构函数。如果你没有声明任何构造函数,编译器也会为你声明一个default构造函数。如你写下面这行代码:
class Empty{};
这就好像你写下了如下的代码:
当然,只有这些函数被调用时,它们才会被编译器创建出来。class Empty{ public: Empty() {} Empty(const Empty &rhs) {} ~Empty() {} Empty& operator=(const Empty &rhs){} };
- 合成的默认复制构造函数与默认构造函数不同,即使定义了其他的构造函数,也会合成复制构造函数。既然会合成默认的复制构造函数那我们干嘛还要学些这个呢?其实我们有的时候还是要定义自己的构造函数,以下两种情况都必须定义复制构造函数,1.经常有一个数据成员是指针或者有成员表示在构造函数中分配的其他资源;2.在创建新对象时必须做一些特定工作。
下面结合一个例子开始介绍复制控制,
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
using std::endl;
struct Example{
//默认构造函数
Example(){cout << "默认构造函数:Example()" << endl;}
//复制构造函数
Example(const Example&){cout << "复制构造函数:const Example&" << endl;}
//赋值操作符
Example& operator=(const Example& rhs)
{
cout << "赋值操作符:operator=(const Example&)" << endl;
return *this;
}
//析构函数
~Example(){cout << "析构函数:~Example()" << endl;}
};
//Example对象作为形参
void func1(Example obj)
{
}
//Example对象引用作为形参
void func2(Example &obj)
{
}
//返回Example对象
Example func3()
{
Example obj;
return obj;
}
int main(void)
{
//调用默认构造函数创建对象tobj
Example tobj;
cout << endl;
//调用复制构造函数,将形参对象obj创建为实参对象tobj的副本
//函数执行完成后,调用析构函数撤销形参对象obj
func1(tobj);
cout << endl;
//形参为实参的引用,无需调用复制构造函数传递实参
func2(tobj);
cout << endl;
//调用默认构造函数创建局部对象obj
//函数返回时调用复制构造函数创建作为返回值副本的临时对象
//调用析构函数撤销局部对象obj
//调用赋值操作符
//执行完赋值操作后,调用析构函数撤销临时对象
tobj = func3();
cout << endl;
//调用默认构造函数动态创建对象
Example *p = new Example;
cout << endl;
//调用默认构造函数创建一个临时对象
//2次调用复制构造函数,将临时对象复制到容器evec的每个元素
//调用析构函数撤销临时对象
vector<Example> evec(2);
cout << endl;
//调用析构函数撤销动态创建的对象
delete p;
cout << endl;
//调用复制构造函数创建数组第一个元素
//调用默认构造函数创建数组第二个元素
Example obj_arr[] = {tobj, Example()};
cout << endl;
//调用5次析构函数撤销对象(evec有2个元素调用2次,数组有两个元素调用2次,tobj调用1次)
return 0;
}
测试结果如下图所示:
这里要介绍的C++11新特性是关于vector容器初始化那部分的结果,上面是用vs2008测试的结果,再看看下面vs2012测试的结果。
很明显我们可以看到,少了默认构造函数创建临时对象,然后通过调用复制构造函数用临时对象来初始化vector容器的每个元素。我一开始用的是vs2012测试的,发现和书上的原理对不上,然后就改用vs2008测试,得到了和书本上原理对应的结果,但是我觉得这两个编译器应该都没有问题,就去网上查了下C++11新增特性、还在论坛上发帖求救,最后的结果是因为C++11的新特性原因。
下面是我摘下的一部分内容:(完整的内容链接:http://www.iteye.com/news/25064)
move语义(move semantics)。简单的说,它是优化复制的一种方式。
有时候复制很显然是浪费的。如果你从一个临时的string对象复制内容,简单的复制指针到字符缓冲区将比创建一个新的缓冲区再复制要高效得多。之所以能工作是因为源对象超出了范围。
然而,在这以前C++并没有判断源对象是不是临时对象的机制。move语义通过除了复制操作外还允许你有一个move构造函数(move constructor)和一个move赋值运算(move assignment)符来提供这个机制。
当你在Visual Studio 2010中使用标准库中的类如string或vector时,它们已经支持move语义了。这可以防止不必要的的复制,从而改善性能。
通过在你的类中实现move语义你可以获得额外的性能提升,比如当你把它们存储到STL容器中时。还有,move语义不仅可以应用到构造函数,还可以应用到方法(如vector的push_back方法)。