当前位置: 首页 > article >正文

真正理解std::move

std::move的作用只有一个,那就是把一个左值强制转换为右值,有了这个右值,右值允许的任何操作就可以实施了。比如:1. 这个右值可以赋给一个左值变量,2. 这个右值可以被一个右值引用来引用。

class A
{
    public:
    A(int n):m_size(n)
    {
        m_ptr=new int[n];

    }
    int m_size;
    int* m_ptr;
    ~A()
    {
        cout<<"   Destructor is called m_ptr="<<m_ptr<<endl;
        delete[] m_ptr;
        m_ptr=nullptr;
    }
    A(A& a)
    {
        cout<<"   copy constructor is called"<<endl;
        m_size=a.m_size;
        m_ptr=new int[m_size];
        

    }
    A(A&& a)        // move constructor
    {
        if(this!=&a)
        {
            m_ptr=a.m_ptr;
            m_size=a.m_size;
            a.m_ptr=nullptr;
        }
         cout<<"   move constructor is called"<<endl;
    }
    A& operator=( A&&r) // move assignment operator
    {
        if(this!=&r)
        {
            delete[] m_ptr;
            m_ptr=r.m_ptr;
            m_size=r.m_size;
            cout<<"   move assignment operator is called"<<endl;
            r.m_ptr=nullptr;
        }
        return *this;
    }
};
int main(int argc, char const *argv[])
{
    A a1(10);    // a1 is a left value

    cout<<"1---will use copy constructor below"<<endl;
    A a2=a1;    //这是定义一个左值a2,并且用另一个左值a1来赋值,拷贝构造函数被调用

    cout<<"2---will use move constructor below"<<endl;
    A a3=move(a1);  // 移动构造函数被调用,在移动构造函数中,a3的m_ptr变量直接指向a1的m_ptr指向的内存,
    // 这样,对象a3不需要重新分配内存,而是直接使用a1.m_ptr指向的内存。
    // 于是a1.m_ptr指向的内存就被移动走了,逻辑上,a1.m_ptr指向必须为空了。
    // 为啥多此一举呢,直接操作a1不就行了???????

    cout<<"a1.m_ptr=="<<a1.m_ptr<<"  显示被移走了"<<endl;
    
    //a1被move了之后,依然是个左值, so any operation appled to a left value will be applied to a1 as well.
    // 下面我们给a1重新赋值
    cout<<"3---will use move assignment operator below"<<endl;
    a1=A(40);       // 右值赋给一个已经定义的左值,于是移动赋值运算符被调用
    cout<<"4-----旧的a1生命周期结束,这是旧的a1的析构函数被调用"<<endl;
    // till now, there are 3 objects alive, a1(A(40)),a2,a3, 在程序退出后,这3个对象需要销毁,
    // 于是析构函数被调用4次  

    cout<<"5--生命周期结束,销毁3个对象"<<endl;  


    return 0;
}

执行结果如下,仔细看一下吧

下面我详细说明一个std::swap的代码实现原理。

// std::swap函数的实现
template<class T>
void swap(T &x, T& y)
{
	T temp = std::move(x);
	x = std::move(y);
	y = std::move(temp);
}

第一行代码:T temp = std::move(x); 调用类型T的移动构造函数,使得temp指向x的资源(包括temp的值等于x的值),这个时候x依然是个左值

第二行 代码:x = std::move(y); 让x指向y的资源(包括x等于y的值)

第三行代码:y = std::move(temp); 让y指向temp的资源(包括y等于temp的值,也就是最初的x的值)

执行结果如下:可以看到,执行swap函数后,a1和a2进行了交换。上面红色方框中的a1的值,与a2进行了交换。

在看一下swap函数内部的执行情况,黄色数字1,2,3,4

黄色数字1表示 执行T temp = std::move(x),这里移动构造函数被调用

黄色数字2表示 执行x = std::move(y);,这里移动赋值函数被调用

黄色数字3表示 执行y = std::move(temp),这里移动赋值函数被调用

黄色数字4表示函数退出的时候,临时变量temp需要销毁,于是析构函数被调用

执行结果的最后两个表示a1和a2被析构。

最后有几点需要说明:

1. std::move和移动构造函数、移动赋值运算符没有任何联系。

2.移动构造函数、移动赋值运算符的调用和一个右值赋值给一个左值的形式有关系。比如:

A a1(10);

A a3=move(a1)

上述情况在定义a3的同时,给赋一个右值,这时移动构造函数被触发。

如果是:

A a3(10);

a3=A(20);

这是一个右值被赋给一个已经创建成功的左值变量,很显然,这时移动赋值运算符被触发。

3. move也和右值引用没有任何联系,可以参考这篇博文:理解C++中的右值引用-CSDN博客

4. 一个左值被move之后,这个左值还能被使用吗?不能被使用,因为这个左值指向的资源可能都被“move”走了,比如:

A a1(10);

A a3=move(a1)

a1被move之后,它的指针已经为空指针了,所以操作a1,可能会得到不可预测的结果。但是a1可以被重新赋值,比如:

a1=A{100),之后a1就又是一个正常的左值了。


http://www.kler.cn/a/522263.html

相关文章:

  • ES2021+新特性、常用函数
  • 【MQ】RabbitMq的可靠性保证
  • Android createScaledBitmap与Canvas通过RectF drawBitmap生成马赛克/高斯模糊(毛玻璃)对比,Kotlin
  • 洛谷P3884 [JLOI2009] 二叉树问题(详解)c++
  • [蓝桥杯 2014 省 AB] 蚂蚁感冒
  • 探秘 TCP TLP:从背景到实现
  • < OS 有关 > 阿里云 几个小时前 使用密钥替换 SSH 密码认证后, 发现主机正在被“攻击” 分析与应对
  • 关于Dubbo的面试题概念原理配置及代码
  • 【信息系统项目管理师-选择真题】2012上半年综合知识答案和详解
  • 十三先天记
  • 【面试】【前端】前端浏览器考题总结
  • 深度学习|表示学习|卷积神经网络|输出维度公式|15
  • HTML 符号详解
  • 安全策略初始实验
  • 30289_SC65XX功能机MMI开发笔记(ums9117)
  • 深入理解动态规划(dp)--(提前要对dfs有了解)
  • 【OMCI实践】ONT上线过程的omci消息(二)
  • leetcode 209. 长度最小的子数组
  • AI 模型评估与质量控制:生成内容的评估与问题防护
  • Web开发 -前端部分-CSS3新特性
  • unity学习20:time相关基础 Time.time 和 Time.deltaTime
  • 基于Django的微博舆情分析系统的设计与实现
  • 【算法与数据结构】动态规划
  • RTOS面试合集
  • 【Python实现机器遗忘算法】复现2020年顶会CVPR算法Selective Forgetting
  • 006 mybatis关联查询(一对一、一对多)