移动构造函数
1.移动语义
c++11新引入了右值引用和移动语义两个概念。
1.1 右值引用
C++(包括C)中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,可以在多条语句中使用的对象。右值是指临时的对象,它们只在当前的语句有效。在C++11之前,右值是不能被引用的。如int &a =1; //无法从“int”转化为“int&”。 我们最多只能用常量引用来绑定一个右值。因为规定不允许修改右值。在C++11中,可以引用右值,使用&&实现:int &&a = 1; //OK.
在理解右值引用之前,先理解临时对象(临时变量)的概念。
临时对象是编译器在编译代码过程中为了实现某些代码可能会产生出一些临时对象来满足一些效果。产生临时变量的地方可能有以下几种:
- 不同类型(对象)变量的转换
- 函数以pass by value传递参数的时候
- 表达式求值中
规定不允许修改临时变量,所以在没有引入移动构造函数的时候,拷贝构造的参数为const &类型,以支持通过临时对象来构造对象。临时对象在函数返回后很快就会销毁,为了能物尽其用而达到节省时间(进行对象拷贝)的效果,考虑将临时对象纳为己有,这样能节省内存开辟和繁琐的赋值时间。
如:
MyString(……):_ptr(mystr._ptr)
{
mystr._ptr = NULL;
}
将_ptr指向的真正的字符串的所有权拿过来了。括号里空出来的参数应该是临时对象,在c++1.0中,无法判断什么时候是临时对象。为此,c++11引入了一个新的概念——右值引用(&&)。右值引用专门用于引用右值(临时对象、匿名对象(即没有名字的对象))。
所以上述构造函数可改写为:
MyString(MyString &&mystr):_ptr(mystr._ptr)
{
myptr._ptr = NULL;
}
当传入临时变量(右值)时,编译器会调用MyString(MyString &&mystr)版本。在函数中,临时对象的指针置为NULL,这很重要,防止临时对象析构时候销毁字符串,使之成为悬挂指针。
2.拷贝构造函数与移动构造函数
拷贝构造是开辟一个新空间然后对临时对象(函数参数)进行深度拷贝,返回该新对象。
移动构造不另外开辟新空间,直接偷走临时对象的内存空间,占为己有。
移动构造的优点:节省了开辟内存与赋值的时间。
3.移动构造函数
程序员提供移动构造函数(参数为右值引用的构造函数)使得当临时变量构造新对象时可以提供较好的优化。提供移动构造函数在某些情况下对性能是极大的提供,尤其是对于需要深拷贝的对象来说。当这一类对象配合标准库的容器的时候(vector、deque),如果提供移动构造函数,将会在容器内存重分配时带来极大效率的优化。
4.移动构造函数的调用
用到临时对象(右值)的时候就会执行移动语义。除了编译器创建的临时对象作为右值外,使用std::move也能得到一个左值的右值引用。