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()
{
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 + ];
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();
}

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);

3. forward

  forward将左值转换为右值:

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

4. 综合示例

#include "stdio.h"
#include<iostream> #include<vector>
using namespace std; class A
{
public:
A() :m_ptr(NULL), m_nSize(){}
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[] = { , , };
A a(arr, sizeof(arr)/sizeof(arr[]));
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{ , , , , };
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;
}

最新文章

  1. 对于挑战书上的很久之前都看不懂的DP看懂的突破
  2. go 字符变量
  3. 再谈Bellman-Ford
  4. C# 4.0中dynamic的作用
  5. nrf51822-主从通信分析1
  6. Remap BMW F11 2010 all ECUs with E-Sys and ENET cable
  7. UnicodeEncodeError: &lsquo;ascii&rsquo; codec can&rsquo;t encode characters in position xxx ordinal
  8. WebApp遇到的一些坑
  9. struts2 标签库 介绍
  10. unity 嵌入 百度分享 与 游戏内购物 iap
  11. Codeforces Round #263 (Div. 1) C. Appleman and a Sheet of Paper 树状数组暴力更新
  12. [Leetcode][021] Merge Two Sorted Lists (Java)
  13. 表格无边框,有内框,在table嵌套时,防止出现重复边线
  14. 企业架构研究总结(29)——TOGAF架构内容框架之概述及架构工作产品分类
  15. OpenGL4.x不支持gluPerspective函数。故备份之
  16. PHP数据访问增删查(20161028)
  17. Ecshop去掉模版中随机出现Ecshop版权的方法
  18. 在ASP.NET开发中一些单词的标准缩写
  19. Chrome及Firefox插件
  20. shell if [[ ]]和[ ]区别 || &amp;&amp;

热门文章

  1. hadoop09----线程池
  2. CAS单点登录的配置
  3. [转]让你从零开始学会写爬虫的5个教程(Python)
  4. MySQL-5.7 存储过程及函数
  5. MySQL-5.7设置InnoDB表数据文件存储位置
  6. 《React-Native系列》3、RN与native交互之Callback、Promise
  7. PAT1042. Shuffling Machine (20)
  8. G1垃圾回收器参数配置
  9. Nginx配置端口访问的网站
  10. 初探UiAutomator2.0中使用Xpath定位元素