C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

1.右值引用个人认为右值引用的目的主要是为了是减少内存拷贝,优化性能。比如下面的代码:str为临时对象,然后调用Stringd的拷贝构造函数,将临时对象的值赋值给String,这种拷贝是完全没

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

1. 右值引用

  个人认为右值引用的目的主要是为了是减少内存拷贝,优化性能。

  比如下面的代码:

String Fun()
{
    String str = "hello world";
    return str;
}

  str为临时对象,然后调用Stringd的拷贝构造函数,将临时对象的值赋值给String,这种拷贝是完全没有必要的,如果堆内存很大,那么这个拷贝构造的代价会很大,带来了额外的性能损耗

  为了避免链式对象的拷贝构造,我们可以使用右值引用拷贝的方式来实现:

MyString& operator=(MyString&& other)
    {
        cout << "MyString& operator=(const MyString&& other)" << endl;
        if (this != &other)
        {
            m_nLen = other.m_nLen;
            m_pData = other.m_pData;
            other.m_pData = NULL;
        }

        return *this;
    }

  上面的代码只是进行了指针权限的转移,而没有额外的性能消耗。

1.1 使用右值引用实现MyString类

#include "stdio.h"
#include <string>
#include <iostream>
using namespace std;

class MyString
{
public:
    MyString() :m_pData(NULL), m_nLen(0)
    {
        cout << "MyString()" << endl;
    }
    MyString(const char *pStr)   // 允许隐式转换
    {
        cout << "MyString(const char *pStr)" << endl;
        m_nLen = strlen(pStr);
        CopyData(pStr);
    }
    MyString(const MyString& other)
    {
        cout << "MyString(const MyString& other)" << endl;
        if (!other.m_pData)
        {
            m_nLen = other.m_nLen;
            DeleteData();
            CopyData(other.m_pData);
        }
    }
    MyString& operator=(const MyString& other)
    {
        cout << "MyString& operator=(const MyString& other)" << endl;
        if (this != &other)
        {
            m_nLen = other.m_nLen;
            DeleteData();
            CopyData(other.m_pData);
        }

        return *this;
    }

    MyString(MyString&& other)
    {
        cout << "MyString(MyString&& other)" << endl;
        m_nLen = other.m_nLen;
        m_pData = other.m_pData;
        other.m_pData = NULL;
    }

    MyString& operator=(MyString&& other)
    {
        cout << "MyString& operator=(const MyString&& other)" << endl;
        if (this != &other)
        {
            m_nLen = other.m_nLen;
            m_pData = other.m_pData;
            other.m_pData = NULL;
        }

        return *this;
    }

    ~MyString()
    {
        DeleteData();
    }

private:
    void CopyData(const char *pData)
    {
        if (pData)
        {
            m_pData = new char[m_nLen + 1];
            memcpy(m_pData, pData, m_nLen);
            m_pData[m_nLen] = '
#include "stdio.h" #include <string> #include <iostream> using namespace std; class MyString { public: MyString() :m_pData(NULL), m_nLen(0) { cout << "MyString()" << endl; } MyString(const char *pStr) // 允许隐式转换  { cout << "MyString(const char *pStr)" << endl; m_nLen = strlen(pStr); CopyData(pStr); } MyString(const MyString& other) { cout << "MyString(const MyString& other)" << endl; if (!other.m_pData) { m_nLen = other.m_nLen; DeleteData(); CopyData(other.m_pData); } } MyString& operator=(const MyString& other) { cout << "MyString& operator=(const MyString& other)" << endl; if (this != &other) { m_nLen = other.m_nLen; DeleteData(); CopyData(other.m_pData); } return *this; } MyString(MyString&& other) { cout << "MyString(MyString&& other)" << endl; m_nLen = other.m_nLen; m_pData = other.m_pData; other.m_pData = NULL; } MyString& operator=(MyString&& other) { cout << "MyString& operator=(const MyString&& other)" << endl; if (this != &other) { m_nLen = other.m_nLen; m_pData = other.m_pData; other.m_pData = NULL; } return *this; } ~MyString() { DeleteData(); } private: void CopyData(const char *pData) { if (pData) { m_pData = new char[m_nLen + 1]; memcpy(m_pData, pData, m_nLen); m_pData[m_nLen] = '\0'; } } void DeleteData() { if (m_pData != NULL) { delete[] m_pData; m_pData = NULL; } } private: char *m_pData; size_t m_nLen; }; MyString Fun() { MyString str = "hello world"; return str; } void main() { MyString str1 = "hello"; MyString str2(str1); MyString str3 = Fun(); }
'; } } void DeleteData() { if (m_pData != NULL) { delete[] m_pData; m_pData = NULL; } } private: char *m_pData; size_t m_nLen; }; MyString Fun() { MyString str = "hello world"; return str; } void main() { MyString str1 = "hello"; MyString str2(str1); MyString str3 = Fun(); }

C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

1.2 右值引用总结

C++11中引入了右值引用和移动语义,可以避免无谓的复制,提高了程序的性能,右值引用标记为T&&。

(1)左值和右值是独立于它们的类型,右值引用类型可能是左值也可能是右值

(2)auto&&或函数参数类型的自动推导的T&&是一个未定的引用类型,它可能是左值引用,也可能是右值引用,取决于初始化的值类型

(3)所有的右值引用叠加到右值引用上仍然是一个右值引用,其它引用叠加都为坐值引用,当T&&为模版参数时,输入左值,它会变为左值引用,输入右值则变为具名的右值引用

(4)编译器会将已命名的右值引用视为左值,而将未命名的右值视为右值

2. move语义

  我们知道移动语义是通过右值引用来匹配临时值的,那么,普通的左值是否也能借组移动语义来优化性能呢?C++11为了解决这个问题,提供了std::move()方法来将左值转换为右值,从而方便应用移动语义。move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转义,没有内存拷贝。

MyString str1 = “hello”;
MyString str2(str1);
MyString str3 = Fun();
MyString str4 = move(str2);

C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

3. forward

  forward将左值转换为右值:

MyString str1 = "hello";
MyString str2(str1);
MyString str3 = Fun();
MyString str4 = move(str2);
MyString str5(forward<MyString>(str3));

C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

4. 综合示例

#include "stdio.h"
#include<iostream>

#include<vector>
using namespace std;

class A
{
public:
    A() :m_ptr(NULL), m_nSize(0){}
    A(int *ptr, int nSize)
    {
        m_nSize = nSize;
        m_ptr = new int[nSize];
        if (m_ptr)
        {
            memcpy(m_ptr, ptr, sizeof(sizeof(int) * nSize));
        }
    }
    A(const A& other)   // 拷贝构造函数实现深拷贝
    {
        m_nSize = other.m_nSize;
        if (other.m_ptr)
        {
            delete[] m_ptr;
            m_ptr = new int[m_nSize];
            memcpy(m_ptr, other.m_ptr, sizeof(sizeof(int)* m_nSize));
        }
        else
        {
            m_ptr = NULL;
        }
        cout << "A(const int &i)" << endl;
    }

    // 右值应用构造函数
    A(A &&other)    
    {
        m_ptr = NULL;
        m_nSize = other.m_nSize;
        if (other.m_ptr)
        {
            m_ptr = move(other.m_ptr);  // 移动语义
            other.m_ptr = NULL;
        }
    }

    ~A()
    {
        if (m_ptr)
        {
            delete[] m_ptr;
            m_ptr = NULL;
        }
    }

    void deleteptr()
    {
        if (m_ptr)
        {
            delete[] m_ptr;
            m_ptr = NULL;
        }
    }

    int *m_ptr;
    int m_nSize;
};

void main()
{
    int arr[] = { 1, 2, 3 };
    A a(arr, sizeof(arr)/sizeof(arr[0]));
    cout << "m_ptr in a Addr: 0x" << a.m_ptr << endl;
    A b(a);
    cout << "m_ptr in b Addr: 0x" << b.m_ptr << endl;
    b.deleteptr();
    A c(std::forward<A>(a));   // 完美转换
    cout << "m_ptr in c Addr: 0x" << c.m_ptr << endl;
    c.deleteptr();

    vector<int> vect{ 1, 2, 3, 4, 5 };
    cout << "before move vect size: " << vect.size() << endl;

    vector<int> vect1 = move(vect);
    cout << "after move vect size: " << vect.size() << endl;
    cout << "new vect1 size: " << vect1.size() << endl;
}

C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120131.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • android生成coredump,详解coredump–全面

    From:http://blog.csdn.net/tenfyguo/article/details/8159176一,什么是coredump我们经常听到大家说到程序core掉了,需要定位解决,这里说的大部分是指对应程序由于各种异常或者bug导致在运行过程中异常退出或者中止,并且在满足一定条件下(这里为什么说需要满足一定的条件呢?下面会分析)会产生一个叫做core的文件。通常情况下,core文件…

  • Java JSON格式字符串转JSON数组与JSON

    Java JSON格式字符串转JSON数组与JSON&lt;!–json-lib–&gt;&lt;dependency&gt;&lt;groupId&gt;net.sf.json-lib&lt;/groupId&gt;&lt;artifactId&gt;json-lib&lt;/artifactId&gt;&lt;version&gt;2.4&lt;/version&gt;&amp

  • win10电脑设置提醒任务_win10添加计划任务

    win10电脑设置提醒任务_win10添加计划任务博主公司周报漏交一次要缴纳50RMB部门经费,另外博主每天上午下午都需要活动10分钟(好像放风。。),防止职业病+让自己的工作状态更好。步骤:1、打开Win10控制面板—>点选管理工

  • 十大经典思维面试题_JS面试题大全

    十大经典思维面试题_JS面试题大全年轻人你不讲武德,耗子尾汁~~~总结就是为了形成自己的js知识网,提升自己,加油!开始干1、介绍js的基本数据类型  答:Undefined、Null、Boolean、Number、String2、js有哪些内置对象? 答:数据封装类对象:Object、Array、Boolean、Number和String  其他对象:Function、Arguments、Math、Date、RegExp、Error  3、this对象的理解 答:this总是指向函数的直接调用者(而非间接调用者

  • sublime激活码-激活码分享

    (sublime激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.htmlS32PGH0SQB-eyJsaWN…

  • activity 工作流程引擎-如何画流程图

    activity 工作流程引擎-如何画流程图前言:activity工作流引擎是当前最流行的工作流,最近公司一直在用这个感觉还是很好用的,学习过程中也有不少的坑所以简单记录一下相关知识的学习过程吧如何画流程图,这里使用浏览器端的一个工具和eclipse里面操作都差不多吧1.流程图里面的节点,如开始,子流程等都是通过拖拽的方式加载的2.一个节点通过点击即可选择下一个节点事件3.如果一个节点出现分支,或者判断的情况可以在流程线上设置…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号