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] = '\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();
}

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)

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【OpenGrok代码搜索引擎】一、OpenGrok简介

    【OpenGrok代码搜索引擎】一、OpenGrok简介OpenGrok是一个快速的、非常有用的代码搜索和交叉引用引擎。它由Java语言编写而成。它可以帮助你快速的搜索,交叉引用和索引你的代码树。它可以很好的识别多种应用程序文件格式和代码版本控制工具。

    2022年4月30日
    43
  • 取反是什么意思_1取反是多少

    取反是什么意思_1取反是多少很多编程语言使用~作为一元按位取反(NOT)操作符,Go重用^XOR操作符来按位取反://错误的取反操作funcmain(){fmt.Println(~2)//bitwisecomplementoperatoris^}//正确示例funcmain(){varduint8=2fmt.Printf(“%08b\n”,d)//00000010fmt.Printf(“%08b\n”

    2022年8月14日
    7
  • mysql长轮询_ajax的轮询和长轮询

    mysql长轮询_ajax的轮询和长轮询概念:轮询(polling):客户端按规定时间定时像服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接。概念总是枯燥的,只有代码方能解心头之快前段代码:index.html:vargetting={url:’server.php’,dataType:’json’,success:function(res){console.log(res);}};//关键在这里,Ajax定时…

    2022年10月9日
    0
  • printwriter报错_java输出print和println

    printwriter报错_java输出print和println我是一位长期读者,但是第一次写作.我目前正在尝试在代码库中使用AspectJ实现一个记录器.AspectJ似乎运行良好,但我遇到了非常奇怪的Java错误.我是一个长期的C和.Net开发人员,他仍然适应Java的世界,所以如果这是一个愚蠢的问题我很抱歉.我的代码试图捕获异常,并将相关信息记录到文本文件中.陷阱工作正常,但我注意到,当我部署时,我没有得到任何数据.我在Java反编译器中打开了我的类文…

    2022年8月10日
    6
  • js估算一篇文章的阅读时长

    js估算一篇文章的阅读时长

    2021年6月6日
    94
  • Java strictfp[通俗易懂]

    Java strictfp[通俗易懂]Javastrictfp关键字的作用strictfp关键字可以应用于方法,类和接口。strictfp关键字的非法代码Javastrictfp关键字确保您将在每个平台上获得相同的结果,如果在浮点变量中执行操作。不同平台的精度可能不同,这就是为什么java编程语言提供了strictfp关键字,它用于在每个平台上获得相同的结果。所以,现在我们就可以更好的控制浮点数据类型运算了。strictf…

    2022年10月21日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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