C++ 11 move constructor 什么时候调用?

C++11支持移动语义。ios

一:为何须要移动语义和什么是移动语义函数

咱们先来看看C++11以前的复制过程。假设有下列代码:优化

vector<string> v1(1000000);//v1存放着100W个string,假设每一个string长度为1000spa

vector<string> v2(v1);//使用v1初始化v2.net

vector和string类都使用动态内存分配,所以他们必须定义使用他们本身的new版本的复制构造函数。code

复制构造函数vector<string>将使用new给1000W个string分配对象,而每一个string 将使用new给每一个string分配1000个字符的空间大小。接下来所有的都将从v1中逐个复制string到v2中,这里的工做量很是大,可是并无问题。orm

但真的没有问题吗?有时候答案是否认的。例如,假设有一个函数,他返回一个vector<string>对象。对象

vector<string>copyVector(const vector<string> &v){blog

vector<string> temp;ip

//复制100W个string到temp

return temp;

}

接下来,以如下方式调用这个函数。

vector<string> v1(1000000);//v1存放着100W个string,假设每一个string长度为1000

vector<string> v2=copyVector(v1);//使用v1初始化v2

构造v2的时候,编译器先利用v1构造生成了一个temp副本,而后将temp复制给一个临时对象,返回给v2,v2利用该临时对象,构造本身。

这将致使很是巨大的工做量!作了大量的无用功(将temp复制给一个临时对象,返回给v2,v2利用该临时对象,构造本身)。在这以后,temp这个临时的对象被删除了,返回的那个temp副本临时对象也被删除了,若是编译器可以将temp的全部权直接转移给v2不是更好吗?也就是说,不用将100W个string屡次复制到新的地方,再删除原来的字符,而是直接保留字符,并将v2与之关联。这相似于计算机中文件的移动。实际文件还保留在原来的地方,而只是记录修改了,这种方法称之为移动语义

移动语义避免了移动原始数据,而只是修改了记录。

要实现移动语义,必须让编译器知道何时须要复制,何时不须要复制。这就是右值引用发挥最大做用的地方


二:如何实现移动语义

看一个简单的使用移动语义的例子。

 1 #include <iostream>
 2  using  namespace std;
 3  class A{
 4      private:
 5          int data; // data
 6           int *pi; // point to data
 7       public:  
 8          // 禁止隐式转换
 9          A(){
10         }
11          explicit A( int i):data(i){
12             cout<< " normal constuctor! "<<endl;
13             pi=&data;
14         }
15         A( const A &a){
16             data=a.data;
17             cout<< " copy constructor! "<<endl;
18             pi=&data;
19         }
20 
21         A(A &&a){
22             cout<< " move constructor! "<<endl;
23              // 直接移动a.pi到pi
24              pi=a.pi;
25              data = a.data;  // 修改源pi
26              a.pi=nullptr;
27             a.data= 0;
28         }
29          // A(A &&a)=delete;
30          A  operator+( const A &a){
31             A temp(data+a.data);
32             cout<<endl<< " operator+ called!show temp! "<<endl;
33             temp.show();
34             cout<<endl;
35              return temp;
36         }
37          void show() const{
38             cout<< " pi= "<<pi<< "    data= "<<data<<endl;
39         }
40 };
41 
42  int main()
43 {
44      int i= 99;
45     A a( 10);
46     a.show();
47     A b(i);
48     b.show();
49     A c(b);
50     c.show();
51     A d(b+c);
52     cout<< " show d! "<<endl;
53     d.show();
54 
55 

56 } 

 

 

 

 

运行截图:

 

在CODE上查看代码片

看出来什么问题没有?

对,好像并无调用移动构造函数!

可是有没有发现!temp和d的pi都是指向同一个地方那个?这是什么状况?

原来是由于GCC自带的右值语义!

也就是,编译器GCC会帮你自动优化!

那么微软的编译器呢? 

 

也会优化! 

 

不信请看下面例子!咱们利用C++11的delete特性!

#include <iostream>  
using  namespace std;  
class A{  
private:  
     int data; // data  
     int *pi; // point to data  
public:  
     // 禁止隐式转换  
    A(){  
    }  
     explicit A( int i):data(i){  
        cout<< " normal constuctor1! "<<endl;    
        pi=&data;  
    }  
    A( const A &a){  
        data=a.data;  
        cout<< " copy constructor! "<<endl;  
        pi=&data;  
    }  
     /*  
    A(A &&a){ 
        cout<<"move constructor!"<<endl; 
        //直接移动a.pi到pi 
        pi=a.pi; 
        data = a.data;

        //修改源pi 
        a.pi=nullptr; 
        a.data=0; 

    }*/  
    A(A &&a)=delete;  
    A operator+(const A &a){  
        A temp(data+a.data);  
        cout<<endl<<"operator+ called!show temp!"<<endl;  
        temp.show();  
        cout<<endl;  
        return temp;  
    }  
    void show()const{  
        cout<<"pi="<<pi<<"   data="<<data<<endl;  
    }  
};  
int main(){  
    int i=99;  
    A a(10);  
    a.show();  
    A b(i);  
    b.show();  
    A c(b);  
    c.show();  
    A d(b+c);  
    cout<<"show d!"<<endl;  
    d.show();  
      
      
}  

 

运行结果:


也就是说,在return temp;这一句上将要调用A(A&&)这个构造函数;

可是如今这个函数被咱们显式删除了!

b+c也是一个右值!也是须要调用移动构造函数的!

所以上一个例子其实是调用了移动语义的构造函数!

 

那么vs2013呢?有intelisense,你根本没法编译!!

 

 

=================================================================================

相关文章
相关标签/搜索