zoukankan      html  css  js  c++  java
  • C++11中令人吐血的"移动语义"和"新的右值引用"

    本文转载自:https://blog.csdn.net/xuwqiang1994/article/details/79924310

    1.概述

    没有C++11之前我都这样写代码的

    Class* ptr = new Class(xxx);
    

    感觉指针用的挺好的,逻辑清晰,很容易理解。
    C++11出来之后,有了"移动语义"和"新的右值引用"的概念,可以预见以后看代码有多头疼。

    2.左值与右值

    左值(lvalue):代表一个在内存中占有确定位置的对象;
    右值(rvalue):通过排他性来定义,不是lvalue的都属于rvalue,即在不在内存中占有确定位置的表达式;

    2.1 例1:

    int var;
    var = 4;
    4 = var;        //ERROR!
    (var + 10) = 4; //ERROR!
    

    如上, 显然"4、(var + 10)"这些表达式是不能作为左值的。

    2.2 例2:

    int foo() { return 2; }
    int main()
    {
        foo() = 2; //ERROR
        return 0;
    }
    

    这里的foo()也不能作为左值,而C++中的引用(reference)让这成为可能

    2.3 例3:

    int globalvar = 20;
    int& foo()
    {
        return globalvar;
    }
    int main()
    {
        foo() = 10;
        return 0;
    }
    

    这里的"&"当表示引用含义的时候,修饰的需要是一个左值。

    3.不用指针胜似指针的做法

    3.1 疑问

    拿C++11举例,C++11里引入了thread,下面的代码会不会让你感到疑惑?

      std::thread threads[5];
      for (int i=0; i<5; ++i)
        threads[i] = std::thread(pause_thread,i+1);
    

    请问thread的构造函数到底被调用了几次呢?

    3.2 样例1

    我们不考虑thread,看下面这个Intvec代码样例:

    class Intvec
    {
    public:
        explicit Intvec(size_t num = 0)
            : m_size(num), m_data(new int[m_size])
        {
            log("constructor");
        }
    
        ~Intvec()
        {
            log("destructor");
            if (m_data) {
                delete[] m_data;
                m_data = 0;
            }
        }
    
        Intvec(const Intvec& other)
            : m_size(other.m_size), m_data(new int[m_size])
        {
            log("copy constructor");
            for (size_t i = 0; i < m_size; ++i)
                m_data[i] = other.m_data[i];
        }
    
        Intvec& operator=(const Intvec& other)
        {
            log("copy assignment operator");
            Intvec tmp(other);
            std::swap(m_size, tmp.m_size);
            std::swap(m_data, tmp.m_data);
            return *this;
        }
    	
    private:
        void log(const char* msg)
        {
            cout << "[" << this << "] " << msg << "
    ";
        }
    
        size_t m_size;
        int* m_data;
    };
    int main()
    {
        Intvec v2;
        cout << "assigning lvalue...
    ";
        v2 = Intvec(20);
        cout << "ended assigning lvalue...
    ";
        return 0;
    }
    

    运行结果

    $ g++ main2.cpp -o main2 --std=c++11
    $ ./main2
    [0x7ffe204edc50] constructor
    assigning lvalue...
    [0x7ffe204edc60] constructor
    [0x7ffe204edc50] copy assignment operator
    [0x7ffe204edc20] copy constructor
    [0x7ffe204edc20] destructor
    [0x7ffe204edc60] destructor
    ended assigning lvalue...
    [0x7ffe204edc50] destructor
    

    结果调用3次构造函数:
    Intvec v2;
    Intvec(20);
    Intvec tmp(other);

    3.3 样例2:

    我们再加一个成员函数;(不用删除原来的operator=)

        Intvec& operator=(Intvec&& other)
        {
            log("move assignment operator");
            std::swap(m_size, other.m_size);
            std::swap(m_data, other.m_data);
            return *this;
        }
    

    运行结果:

    $ g++ main2.cpp -o main2 --std=c++11
    $ ./main2
    [0x7ffe5aa0ad70] constructor
    assigning lvalue...
    [0x7ffe5aa0ad80] constructor
    [0x7ffe5aa0ad70] move assignment operator
    [0x7ffe5aa0ad80] destructor
    ended assigning lvalue...
    [0x7ffe5aa0ad70] destructor
    

    结果只调用了两次构造函数。
    从外观上看
    Intvec& operator=(Intvec&& other)和
    Intvec& operator=(const Intvec& other)
    在传参上并没有什么不同。但显然编译器知道自己该调用哪个函数。

    4.总结

    "&&"就是C++11支持的"新右值引用操作符",operator=(Intvec&& other)这个函数就是实现"移动语义"的一种方法。

    PS:C++越改越像个脚本语言,图啥?
    从个人角度看,以后写代码,我还是倾向于使用

    Intvec* p2 = new Intvec(20);
    delete p2;
    

    的方式,这只调用一次构造函数,而且逻辑还很清晰,不用考虑类内部的实现。

    Intvec v2 = Intvec(20); //也只调用一次,这就是另外一回事了。
    

    本文转载自:https://blog.csdn.net/xuwqiang1994/article/details/79924310

  • 相关阅读:
    FpSpread实现筛选的功能
    比较通用的ID,ParentID结构表,返回树路径。
    如何解决,在安装Sql2000时,出现程序挂起的解决方法。
    FarPoint Spread 控件如何实现剪切,粘贴,复制
    MySql中Group By和Order By使用的注意事项!
    php md5下16位和32位的实现代码
    Shadows在C#代替方法!
    FTP命令使用详解
    数据库中存放目录与文件结构,方案选择
    vs2008 sp1下载
  • 原文地址:https://www.cnblogs.com/bugutian/p/13860234.html
Copyright © 2011-2022 走看看