右值引用&&
以下内容参考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 ...
随机推荐
- C_数据结构_循环队列
# include <stdio.h> # include <malloc.h> typedef struct Queue { int * pBase; int front; ...
- 冲刺Two之站立会议10
今天是最后一次站立会议,我们为自己软件最终版的发布进行了讨论,针对项目开发过程中出现的问题进行了总结.并讨论了之后软件如何发布和推广.
- 最新广商小助手 项目进展 OpenGL ES 3D在我项目中引用 代码太多只好选重要部分出来
package com.example.home; import java.io.IOException; import java.io.InputStream; import javax.micro ...
- 关于RESTful 的概念
1.REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露.URI 的设计只要负责把资源通过合理方式暴露出来就可以了.对资源的操作与它无关,操作是通过 HTTP动词来体现,所以RES ...
- 理解java的三大特性之继承
学习来源:http://www.cnblogs.com/chenssy/p/3354884.html default 默认权限(包权限-同一个包可以访问) private 私有(类内部可以使用,继承的 ...
- centos7编译安装zabbix的错误
[Z3001] connection to database 'zabbix' failed: [2002] Can't connect to local MySQL server through s ...
- WebLogic Server Components:XA Transactions
https://docs.oracle.com/cd/E19509-01/820-5892/ref_xatrans/index.html Weblogic支持基于JPA规范的强一致性的XA事务,应对传 ...
- SQLserver 一种简单的GUI方式创建DBlink copy 表数据的方法
1. 在sqlserver 上面使用GUI的方式创建dblink 首先打开查询分析器 在如下的位置处右键 -新建连接服务器 输入需要copy数据的服务器 输入ip地址 然后建立连接 在打开查询分析器进 ...
- python3_字符串
一.字符串的表示 >>> s = "narjaja never give up" #字符串的创建和初始化 >>> s = 'narjaja ne ...
- Cmder 常用配置
windows 系统的 cmd 命令窗口不是很好用,可以试试 Cmder 工具包. 1.在运行框中快速启动 Cmder 将 cmder.exe 文件所在目录加载环境变量 PATH 中. 2.把 cms ...