• C++ 11 move constructor 何时调用?


    C++11支持移动语义。

    一:为什么需要移动语义和什么是移动语义

    我们先来看看C++11之前的复制过程。假设有下列代码:

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

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

    vector和string类都使用动态内存分配,因此他们必须定义使用他们自己的new版本的复制构造函数。

    复制构造函数vector<string>将使用new给1000W个string分配对象,而每个string 将使用new给每个string分配1000个字符的空间大小。接下来全部的都将从v1中逐个复制string到v2中,这里的工作量非常大,但是并没有问题。

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

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

    vector<string> temp;

    //复制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,你根本无法编译!!

     

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


  • 相关阅读:
    选择排序
    【洛谷8341】[AHOI2022] 回忆(贪心)
    swagger在高版本springboot中的使用
    springboot中关于连接池的几种方式
    MySQL 的 delete、truncate、drop 有什么区别?
    Docker容器命令
    IDEA 神级插件!效率提升 20 倍!
    Bean Searcher——多条件查询神器
    8 种最坑SQL语法,工作中踩过吗?
    消息中间件:rocketmq单机安装部署
  • 原文地址:https://www.cnblogs.com/likeatree/p/3990991.html
Copyright © 2020-2023  润新知