右值引用&&
以下内容参考https://blog.csdn.net/china_jeffery/article/details/78520237
右值引用若不作为函数参数使用,基本等于滥用
右值引用 (Rvalue Referene) 是 C++ 新标准 中引入的新特性 , 它实现了移动语义 (Move Sementics) 和完美转发 (Perfect Forwarding)。它的主要目的有两个方面:
1. 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
2. 能够更简洁明确地定义泛型函数。
何为右值:
C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,
那些可以在多条语句中使用的对象。 所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。
右值是指临时的对象,它们只在当前的语句中有效。
int i = ;// 在这条语句中,i 是左值,0 是临时变量,就是右值
在C++11之前,右值是不能引的,如
int &a = ;// error : 非常量的引用必须为左值
const int &a = ;// 我们最多只能用常量引用来绑定一个右值
在C++11中我们可以引用右值,使用&&实现:
int &&a = ;
应用场景:
如下string类,实现了拷贝构造和赋值运算符重载
#include <iostream>
#include <vector>
using namespace std; class MyString
{
public:
MyString()
{
m_data = NULL;
m_len = ;
} MyString(const char* s)
{
m_len = strlen(s);
init_data(s);
cout << "构造函数" << s << endl;
} MyString(const MyString& str)
{
m_len = str.m_len;
init_data(str.m_data);
cout << "拷贝构造函数" << str.m_data << endl;
} MyString& operator=(const MyString& str)
{
if ( this != &str ){
this->m_len = str.m_len;
init_data(str.m_data);
}
cout << "等号操作符重载" << str.m_data << endl;
return *this;
} ~MyString()
{
if ( m_data != NULL ){
cout << "析构函数" << endl;
free(m_data);
}
} private:
void init_data(const char* s)
{
m_data = new char[m_len + ];
memcpy(m_data, s, m_len);
m_data[m_len] = '\0';
}
char* m_data;
size_t m_len;
}; void test()
{
vector<MyString> vec;
MyString a;// 没有输出
a = MyString("hello");
vec.push_back(MyString("world"));
} int main()
{
test(); system("pause");
return ;
}
输出:
构造函数hello
等号操作符重载hello
析构函数
构造函数world
拷贝构造函数world
析构函数
析构函数
析构函数
总共执行了2次拷贝,MyString("Hello")和MyString("World")都是临时对象,临时对象被使用完之后会被立即析构,在析构函数中free掉申请的内存资源。
如果能够直接使用临时对象已经申请的资源,并在其析构函数中取消对资源的释放,这样既能节省资源,有能节省资源申请和释放的时间。 这正是定义移动语义的目的。
通过加入定义移动构造函数和转移赋值操作符重载来实现右值引用(即复用临时对象):
#include <iostream>
#include <vector>
using namespace std; class MyString
{
public:
MyString()
{
m_data = NULL;
m_len = ;
} MyString(const char* s)
{
m_len = strlen(s);
init_data(s);
cout << "构造函数" << s << endl;
} MyString(MyString&& str)
{
cout << "移动构造函数" << str.m_data << endl;
m_len = str.m_len;
m_data = str.m_data;
str.m_len = ;
str.m_data = NULL;// 防止在析构函数中将内存释放掉
} MyString& operator=(MyString&& str)
{
cout << "移动等号操作符重载" << str.m_data << endl;
if ( this != &str ){
this->m_len = str.m_len;
this->m_data = str.m_data;
str.m_len = ;
str.m_data = NULL;// 防止在析构函数中将内存释放掉
}
return *this;
} ~MyString()
{
if ( m_data != NULL ){
cout << "析构函数" << endl;
free(m_data);
}
} private:
void init_data(const char* s)
{
m_data = new char[m_len + ];
memcpy(m_data, s, m_len);
m_data[m_len] = '\0';
}
char* m_data;
size_t m_len;
}; void test()
{
vector<MyString> vec;
MyString a;// 没有输出
a = MyString("hello");
vec.push_back(MyString("world"));
} int main()
{
test(); system("pause");
return ;
}
输出:
构造函数hello
移动等号操作符重载hello
构造函数world
移动拷贝构造函数world
析构函数
析构函数
需要注意的是:右值引用并不能阻止编译器在临时对象使用完之后将其释放掉的事实,
所以移动构造函数和移动赋值操作符重载函数 中都将_data赋值为了NULL,而且析构函数中保证了_data != NULL才会释放。
标准库函数std::move
既然编译器只对右值引用才能调用移动构造函数和移动赋值函数,又因为所有命名对象都只能是左值引用。
在这样的条件了,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,
也就是把一个左值引用当做右值引用来使用,怎么做呢?
标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。
#include <iostream>
using namespace std; void ProcessValue(int& i)
{
cout << "LValue processed:" << i << endl;
} void ProcessValue(int&& i)
{
cout << "RValue processed:" << i << endl;
} int main()
{
int a = ;
ProcessValue(a);
ProcessValue(move(a)); system("pause");
return ;
}
输出:
LValue processed:1
RValue processed:1
std::move在提高swap函数性能上有非常大的帮助,一般来说,swap函数的通用定义如下:
template <class T>
void swap(T& a, T& b)
{
T tmp(a);// copy a to tmp
a = b;// copy b to a
b = tmp;//copy tmp to b
}
结合std::move 和 右值引用,可以避免不必要的拷贝。swap的定义变为:
#include <iostream>
#include <vector>
using namespace std; class MyString
{
public:
MyString()
{
m_data = NULL;
m_len = ;
} MyString(const char* s)
{
m_len = strlen(s);
init_data(s);
cout << "构造函数" << s << endl;
} MyString(MyString&& str)
{
cout << "移动构造函数" << str.m_data << endl;
m_len = str.m_len;
m_data = str.m_data;
str.m_len = ;
str.m_data = NULL;// 防止在析构函数中将内存释放掉
} MyString& operator=(MyString&& str)
{
cout << "移动等号操作符重载" << str.m_data << endl;
if ( this != &str ){
this->m_len = str.m_len;
this->m_data = str.m_data;
str.m_len = ;
str.m_data = NULL;// 防止在析构函数中将内存释放掉
}
return *this;
} ~MyString()
{
if ( m_data != NULL ){
cout << "析构函数" << endl;
free(m_data);
}
} private:
void init_data(const char* s)
{
m_data = new char[m_len + ];
memcpy(m_data, s, m_len);
m_data[m_len] = '\0';
}
char* m_data;
size_t m_len;
}; namespace MyT{
template <class T>
void swap(T& a, T& b)
{
T tmp(std::move(a));// move a to tmp
a = std::move(b);// move b to a
b = std::move(tmp);//move tmp to b
}
} void test()
{
MyString a("hello");
MyString b("world");
MyT::swap<MyString>(a, b);
} int main()
{
test(); system("pause");
return ;
}
精确传递(perfect Forwarding)
精确传递就是在参数传递过程中,所有这些属性和参数值都不能改变。在泛型函数中,这样的需求非常普遍
forward_value函数只有一个参数val,定义如下:
template <typename T>
void forward_value(const T& val) {
process_value(val);
} template <typename T>
void forward_value(T& val) {
process_value(val);
}
函数 forward_value 为每一个参数必须重载两种类型,T& 和 const T&,否则,下面四种不同类型参数的调用中就不能同时满足:
int a = ;
const int &b = ;
forward_value(a); // int&
forward_value(b); // const int&
forward_value(); // int&
对于一个参数就要重载两次,也就是函数重载的次数和参数的个数是一个正比的关系。这个函数的定义次数对于程序员来说,是非常低效的。我们看看右值引用如何帮助我们解决这个问题:
template <typename T>
void forward_value(T&& val) {
process_value(val);
}
只需要定义一次,接受一个右值引用的参数,就能够将所有的参数类型原封不动的传递给目标函数。
右值引用&&的更多相关文章
- C++右值引用浅析
一直想试着把自己理解和学习到的右值引用相关的技术细节整理并分享出来,希望能够对感兴趣的朋友提供帮助. 右值引用是C++11标准中新增的一个特性.右值引用允许程序员可以忽略逻辑上不需要的拷贝:而且还可以 ...
- C++ 11 中的右值引用
C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream> #include &l ...
- 图说函数模板右值引用参数(T&&)类型推导规则(C++11)
见下图: 规律总结: 只要我们传递一个基本类型是A④的左值,那么,传递后,T的类型就是A&,形参在函数体中的类型就是A&. 只要我们传递一个基本类型是A的右值,那么,传递后,T的类型就 ...
- c++11的右值引用、移动语义
对于c++11来说移动语义是一个重要的概念,一直以来我对这个概念都似懂非懂.最近翻翻资料感觉突然开窍,因此记下.其实搞懂之后就会发现这个概念很简单,并无什么高深的地方. 先说说右值引用.右值一般指的是 ...
- VS2012 error C2664: “std::make_pair”:无法将左值绑定到右值引用
在vs2012(c++)make_pair()改动: C++: template <class T1, class T2> pair<V1, V2> make_pair(T1& ...
- 右值引用、move与move constructor
http://blog.chinaunix.net/uid-20726254-id-3486721.htm 这个绝对是新增的top特性,篇幅非常多.看着就有点费劲,总结更费劲. 原来的标准当中,参数与 ...
- 【转】C++11 标准新特性: 右值引用与转移语义
VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...
- move语义和右值引用
C++11支持move语义,用以避免非必要拷贝和临时对象. 具体内容见收藏中的“C++右值引用” .
- [转载] C++11中的右值引用
C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...
- C++ 11 右值引用
C++11中引入的一个非常重要的概念就是右值引用.理解右值引用是学习“移动语义”(move semantics)的基础.而要理解右值引用,就必须先区分左值与右值. 注意:左值右值翻译可能有些问题 *L ...
随机推荐
- Python - 内置函数 选例
概览参见 https://www.runoob.com/python/python-built-in-functions.html 官方文档 https://docs.python.org/3/li ...
- Redis数据"丢失"讨论及规避和解决的几点总结
Redis大部分应用场景是纯缓存服务,请求后端有Primary Storage的组件,如MySQL,HBase;请求Redis的键未命中,会从primary Storage中获取数据返回,同时更新Re ...
- PAT甲题题解-1130. Infix Expression (25)-中序遍历
博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789828.html特别不喜欢那些随便转载别人的原创文章又不给 ...
- Microsoft Visual Studio 2013安装及试用
我是在网上下载的vs2013版的安装包,下载的是压缩文件,解压后是2.86GB.安装包下载完成后我们就可以进入安装了. 同时建议最好在互联网连接的情况下安装. 打开下载好的文件,我们要选择.exe可执 ...
- Linux第五周学习总结
作者:黎静 一.知识点总结 (一)给MenuOS增加time和time-asm命令 1.更新menu代码到最新版 2.test.c中main函数里,增加MenuConfig() 3.增加对应的两个函数 ...
- iOS开发线程安全问题
先来看一下代码: - (void)viewDidLoad { [super viewDidLoad]; self.testStr = @"String initial complete&qu ...
- MSA微服务
https://github.com/das2017?tab=repositories https://github.com/icsharpcode/ILSpy/releases LayerDemo ...
- [转帖]论iPhone处理器十年进化史
论iPhone处理器十年进化史 导读: 今天,苹果发布了最新一代的iPhone,作为新一代的旗舰,新手机的功能承载了苹果对未来的希望和消费者的期待.但从我们半导体人看来更关注的是内部技术的演变,尤其是 ...
- jquery中append、prepend, before和after方法的区别
1.prepend() .append()方法在被选元素的开头/结尾插入文本/HTML: 2.before().after()方法在被选元素之前/之后插入内容: append() & prep ...
- SSM三大框架整合配置(Spring+SpringMVC+MyBatis)
web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=" ...