c++新特性实验(4)声明与定义:右值引用(C++11)
1.作用
c++11以前,临时对象、字面常量一般情况下不可以再次访问,也不可以修改。右值引用可以解决这个问题。
1.1 实验A
#include <iostream>
using namespace std; class A{
int id;
public:
A(int i) : id(i){
cout << "A constructor " << id << endl;
}
~A(){
cout << "A destructor " << id << endl;
}
void fun(){
cout << "I'm " << id << endl;
}
}; int
main(int argc,char *argv[]){ char ch = 'a';
{
A(1).fun();
cout << "exit {} " << endl;
} return ;
}
结果:
A constructor 1
I'm 1
A destructor 1
exit {}
问题:
- 代码第21行和第23行的红色部分,一个是字面常量,一个产生A的临时对象。它们的生命期都是一行。
- 如果后续代码还想访问它们怎么办?
- 如果想修改A(1)产生的临时对象的值怎么办?
- 如果后面还有很多类似A(1).fun()这样的调用,每次都要分配空间,调用构造、析构。如果对象复杂点,那么就很费时。
- 临时对象生命期结束后,为什么还要去访问、修改它?不频繁的A(1)这样的调用,不就没有问题4了吗?
1.2 C++11以前解决问题2
用const引用临时对象或者字面量,修改如下。
int
main(int argc,char *argv[]){ {
char ch = 'a';
A().fun();
cout << "exit {} " << endl;
} {
const char ch = 'b';
//A &ref = A(2); //error,非const左值引用不能引用右值。cannot bind non-const lvalue reference of type ‘A&’ to an rvalue of type ‘A’
const A &ref = A();
ref.fun();
cout << "exit {} " << endl;
} return ;
}
同时class A的fun()也要提供const版本。
void fun() const {
cout << "I'm " << id << endl;
}
1.3 c++11以前解决问题3、4
无法解决问题3、4
c++11起,可以用右值引用解决这两个问题,可以修改临时对象的值,也避免重复分配空间。
2.特性(C++11起)
- 它是右值的引用,右值的别名。
- 用两个“&&”声明
- 右值引用只能引用右值(字面常量,临时对象)。
- 右值引用声明时要初始化,成员右值引用要在构造初始化列表中初始化。
- 右值引用被列入函数重载规则
- 它引用的对象在释放前,可以通过它访问、修改(const 右值引用除外)。
- 它与引用占的空间一样。
2.实验B:特性[1-3]
2.1 左值引用字面量
char ch = 'a';
//char &lr = 'e'; //error,非const左值引用不能引用右值
const char &clr = 'b'; //ok
2.2 右值引用字面量
char ch = 'b';
//char && rr2 = ch; //error,ch不是右值。
char && rr1 = 'b'; //ok
char && rr2 = 'b' + getInt2(); //ok,普通函数
char && rr3 = 'b' + * - ^ ; //ok
char && rr4 = 'b' + getInt() / ; //ok,constexpr函数
//char && rr5 = rr4; //error,虽然rr4是合法的右值引用,但是它不是右值。 char *pch = &ch;
char *&& prch1 = &rr4; //ok.
//char *&& prch2 = pch; //error,
prch1 = &ch; //ok.&ch得到ch的地址,它是右值。 cout << " rr1 = " << rr1 << " rr2 = " << rr2 << " rr3 = " << rr3 << " rr4 = " << rr4 << " prch1 = " << *prch1 << endl;
结果
rr1 = b rr2 = l rr3 = e rr4 = i prch1 = b
2.3 右值引用临时对象
A a();
//A &&rr1 = a; //error,a不是临时对象
A().fun();
A && rr2 = {};
A &r1 = rr2; //ok
A *pa = &rr2; //ok
rr2.fun();
r1.fun();
pa->fun();
cout << "exit object test {}" << endl;
A的定义如下:
class A{
public:
void fun() const {
cout << "I'm " << id << endl;
}
public:
int id;
A(int i) : id(i){
cout << "A constructor " << id << endl;
}
virtual ~A(){
cout << "A destructor " << id << endl;
}
A(const A &a){
cout << "A copy constructor ,from " << a.id << endl;
if(&a == this ) return;
this->id = a.id;
}
A& operator=(const A &o){
cout << "A operator= ,from " << o.id << endl;
if(&o == this ) return *this;
this->id = o.id;
return *this;
}
friend ostream& operator<<(ostream &os,const A &a);
};
运行结果:
A constructor 0
A constructor 5
I'm 5
A destructor 5
A constructor 3
I'm 3
I'm 3
I'm 3
exit object test {}
A destructor 3
A destructor 0
3.实验C:右值引用的初始化
右值引用声明时要初始化,成员右值引用要在构造初始化列表中初始化。
//声明时要初始化
char && rr1 ; //error,未初始化。
成员右值引用的初始化。
class B{
public:
char &&rr;
B():rr('d'){
}
B(const B &b):rr('d'){
}
B& operator=(const B &b){
this->rr = b.rr;
return *this;
}
/* error
char&& getRR(){
return rr;
}
*/
};
B b ;
cout << "b.rr = " << b.rr << endl;
运行结果
b.rr = d
4.实验D:右值引用与函数
4.1 右值引用被列入重载参考
class D { };
void
foo(const D &){
cout << " foo & called " << endl;
}
void
foo(const D &&){
cout << " foo&& called " << endl;
}
测试代码
D d;
foo(d);
foo(D());
结果:
foo & called
foo&& called
如果把右值引用重载版本去掉,两个都输出:foo & called
4.2 右值引用与返回值
Rvalue references
Rvalue references can be used to extend the lifetimes of temporary objects (note,
lvalue references to const can extend the lifetimes of temporary objects too,but they are not modifiable through them):
右值引用可以用来为临时对象延长生存期。
那它可以延长多久?能超出{}吗?能像堆上的对象的生命期一样?可以把函数返回值声明右值引用然后返回临时对象?
函数结束后,临时对象空间被释放,运行时会出错。
A&&
fun(){
cout << __func__ << endl;
return A();
}
那如果返回全局的右值引用或者成员函数返回成员右值引用?
char && rrrr = 'd';
char &&
fch(){
return rrrr;
}
或者
class B{
public:
char &&rr;
// error
char&& getRR(){
return rr;
}
};
都不可以。
只能在它所在的 { } 延长一点生存期、出 } 就释放。
5.实验E:用右值引用修改临时对象
A && rr2 = {};
const A &&rr3 = A(); //ok
A &r1 = rr2; //ok
A *pa = &rr2; //ok
rr2.fun();
r1.fun();
pa->fun();
rr2.id = ;
rr2.fun();
r1.fun();
pa->fun();
char && ch = 'd';
cout << "ch = " << ch << endl;
ch = 'e';
cout << "ch = " << ch << endl;
const char && cch = 'f';
//cch = 'g'; //error,const的
6.实验F:右值引用占内存空间吗
6.1 成员引用占空间
在linux 64位、gcc7.4.0 下测试,类里的引用成员与一个指针占空间大小一样。
class F{
};
class E{
public:
char && rr;
E():rr('e'){
}
};
class G{
public:
char *pch;
};
class H{
public:
char &rch;
H(char ch):rch(ch){
}
};
cout << "alignof(F) = " << alignof(F) << "\tsizeof(F) = " << sizeof(F) << endl;
cout << "alignof(E) = " << alignof(E) << "\tsizeof(E) = " << sizeof(E) << endl;
cout << "alignof(G) = " << alignof(G) << "\tsizeof(G) = " << sizeof(G) << endl;
cout << "alignof(H) = " << alignof(H) << "\tsizeof(H) = " << sizeof(H) << endl;
结果
alignof(F) = 1 sizeof(F) = 1
alignof(E) = 8 sizeof(E) = 8
alignof(G) = 8 sizeof(G) = 8
alignof(H) = 8 sizeof(H) = 8
win10_64 + vs2019 结果:
alignof(F) = 1 sizeof(F) = 1
alignof(E) = 4 sizeof(E) = 4
alignof(G) = 4 sizeof(G) = 4
alignof(H) = 4 sizeof(H) = 4
6.2 验证G:引用是指针?
如果引用是const指针,那么它应该有独立的地址。
int a = ;
int &ra = a;
const int * const pa = &a; cout << " a.addr = " << &a << endl;
cout << "ra.addr = " << &ra << endl;
cout << "pa.addr = " << &pa << endl;
cout <<"(*pa).addr = " << pa << endl;
结果
a.addr = 0x7fff3810df64
ra.addr = 0x7fff3810df64
pa.addr = 0x7fff3810df68
(*pa).addr = 0x7fff3810df64
如果ra是个类似pa一样的const指针,它应该和pa一样,有自己的地址,但是对ra取地址时,它和a的地址一样。普通引用不占空间。
7.完整示例
#include <iostream>
using namespace std; class A{
public:
void fun() const {
cout << "I'm " << id << endl;
}
public:
int id;
A(int i) : id(i){
cout << "A constructor " << id << endl;
}
virtual ~A(){
cout << "A destructor " << id << endl;
}
A(const A &a){
cout << "A copy constructor ,from " << a.id << endl;
if(&a == this ) return;
this->id = a.id;
}
A& operator=(const A &o){
cout << "A operator= ,from " << o.id << endl;
if(&o == this ) return *this;
this->id = o.id;
return *this;
}
friend ostream& operator<<(ostream &os,const A &a);
};
ostream& operator<<(ostream &os,const A &a){
os << "A.id = " << a.id << endl;
return os;
} constexpr int
getInt(){
return ;
}
int getInt2(){
int a = ;
return a;
} class D { }; void
foo(const D &){
cout << " foo & called " << endl;
}
void
foo(const D &&){
cout << " foo&& called " << endl;
} A&&
fun(){
cout << __func__ << endl;
return A();
} /*
char && rrrr = 'd';
char &&
fch(){
return rrrr;
}
*/
int
main(int argc,char *argv[]){ {
char ch = 'a';
//char &lr = 'e'; //error,非const左值引用不能引用右值
const char &clr = 'b'; //ok
}
{
char ch = 'b';
//char && rr2 = ch; //error,ch不是右值。
char && rr1 = 'b'; //ok
char && rr2 = 'b' + getInt2(); //ok,普通函数
char && rr3 = 'b' + * - ^ ; //ok
char && rr4 = 'b' + getInt() / ; //ok,constexpr函数
//char && rr5 = rr4; //error,虽然rr4是合法的右值引用,但是它不是右值。 char *pch = &ch;
char *&& prch1 = &rr4; //ok.
//char *&& prch2 = pch; //error,
prch1 = &ch; //ok.&ch得到ch的地址,它是右值。 cout << " rr1 = " << rr1 << " rr2 = " << rr2 << " rr3 = " << rr3 << " rr4 = " << rr4 << " prch1 = " << *prch1 << endl;
}
{
A a();
//A &&rr1 = a; //error,a不是临时对象
A().fun();
A && rr2 = {}; //ok
const A &&rr3 = A(); //ok
A &r1 = rr2; //ok
A *pa = &rr2; //ok rr2.fun();
r1.fun();
pa->fun(); cout << "exit object test {}" << endl;
}
{
//声明时要初始化
//char && rr1 ; //error,未初始化。 class B{
public:
char &&rr;
/* error
char&& getRR(){
return rr;
}
*/
B():rr('d'){
}
B(const B &b):rr('d'){
}
B& operator=(const B &b){
this->rr = b.rr;
return *this;
} /* error
char&& getRR2(){
return rrrr;
}
*/
};
B b ;
cout << "b.rr = " << b.rr << endl; }
{
D d;
foo(d);
foo(D()); A && rr = fun();
//cout << " rr = " << rr << endl;
} {
A && rr2 = {};
const A &&rr3 = A(); //ok
A &r1 = rr2; //ok
A *pa = &rr2; //ok rr2.fun();
r1.fun();
pa->fun(); rr2.id = ; rr2.fun();
r1.fun();
pa->fun(); pa->id = ; rr2.fun();
r1.fun();
pa->fun(); char && ch = 'd';
cout << "ch = " << ch << endl;
ch = 'e';
cout << "ch = " << ch << endl; const char && cch = 'f';
//cch = 'g'; //error,const的
} {
class F{
};
class E{
public:
char && rr;
E():rr('e'){
}
};
class G{
public:
char *pch;
};
class H{
public:
char &rch;
H(char ch):rch(ch){
}
}; cout << "alignof(F) = " << alignof(F) << "\tsizeof(F) = " << sizeof(F) << endl;
cout << "alignof(E) = " << alignof(E) << "\tsizeof(E) = " << sizeof(E) << endl;
cout << "alignof(G) = " << alignof(G) << "\tsizeof(G) = " << sizeof(G) << endl;
cout << "alignof(H) = " << alignof(H) << "\tsizeof(H) = " << sizeof(H) << endl;
} {
int a = ;
int &ra = a;
const int * const pa = &a; cout << " a.addr = " << &a << endl;
cout << "ra.addr = " << &ra << endl;
cout << "pa.addr = " << &pa << endl;
cout <<"(*pa).addr = " << pa << endl; }
return ;
}
c++新特性实验(4)声明与定义:右值引用(C++11)的更多相关文章
- c++新特性实验(5)声明与定义:属性列表(C++11 起)
1.初识属性 1.1 实验A: noreturn 属性 [[ noreturn ]] static void thread1(void *data){ cout << "nore ...
- c++新特性实验(3)声明与定义:constexpr
1.作用 constexpr 声明一个函数或变量,它的值可以在编译时出现在常量表达式之中. 2.constexpr 变量要求 其类型必须是 字面类型 (LiteralType) . 它必须被立即初始化 ...
- c++新特性实验(2)类型特性
1. 基本类型 1.1 增加 long long long long int signed long long signed long long int unsigned long long unsi ...
- 【转】C++11 标准新特性: 右值引用与转移语义
VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...
- C++11 标准新特性: 右值引用与转移语义
文章出处:https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/ 新特性的目的 右值引用 (Rvalue Referene) ...
- C++ 新特性 笔记 2 右值引用
C ++ Rvalue引用说明 以下内容,主要是上述链接的摘要 介绍 Rvalue引用是C ++的一个特性,它是随C ++ 11标准添加的.使右值参考有点难以理解的是,当你第一次看到它们时,不清楚它们 ...
- 透彻理解C++11新特性:右值引用、std::move、std::forward
目录 浅拷贝.深拷贝 左值.右值 右值引用类型 强转右值 std::move 重新审视右值引用 右值引用类型和右值的关系 函数参数传递 函数返还值传递 万能引用 引用折叠 完美转发 std::forw ...
- C++11新特性:右值引用和转移构造函数
问题背景 #include <iostream> using namespace std; vector<int> doubleValues (const vector< ...
- C++ 新特性-右值引用
作为最重要的一项语言特性,右值引用(rvalue references)被引入到 C++0x中.我们可以通过操作符“&&”来声明一个右值引用,原先在C++中使用“&”操作符声明 ...
随机推荐
- 在 U-BOOT 对 Nand Flash 的支持
1.1 U-BOOT 对从 Nand Flash 启动的支持 1.1.1 从 Nand Flash 启动 U-BOOT 的基本原理 1. 前 4K 的问题 如果 S3C2410 被配置成从 ...
- Ping- Ip- Linux必学的60个命令
1.作用 ping检测主机网络接口状态,使用权限是所有用户. 2.格式 ping [-dfnqrRv][-c][-i][-I][-l][-p][-s][-t] IP地址 3.主要参数 -d:使用Soc ...
- JS规则 编程练习 考考大家的数学,计算以下计算公式的结果。然后在浏览器中运行一下,看看结果是否跟你的结果一致。
编程练习 考考大家的数学,计算以下计算公式的结果.然后在浏览器中运行一下,看看结果是否跟你的结果一致. 任务 第一步: 在 ? 处填写你的答案. 第二步: 填写完成后,运行一下,看看是不是跟你填写的 ...
- vue-cli3使用yarn run build打包找不到路径
vue-cli3使用yarn run build打包项目部署到服务器上面,运行空白 解决办法非常方便,直接创建vue.config.js 在vue.config.js中添加即可 再打包项目即成功
- bzoj4144 Petrol
题意:给你一张n个点m条边的带权无向图.其中由s个点是加油站.询问从x加油站到y加油站,油箱容量<=b,能否走到? n,m,q,s<=20W,b<=2e9. 标程: #include ...
- 集合划分——cf1028D思维题
非常思维的一道题目,题意很长 给定s1,s2两个集合,s1维护最大值,s2维护最小值,s1的所有元素要比s2小 操作1:往两个集合里的任意一个添加x 操作2:把x从所在的集合里删掉:要求被删的x必须是 ...
- webstorm中使用git管理服务器上的代码——入门级
一.首先要确保电脑已经成功安装好git了.(记住git的安装位置) 二.这里需要给webstorm配置一下:依次点击:file –> Settings –> Version Control ...
- MS-coco数据集下载及使用(转)
先做个标记,改天研究下. 几个链接: MS coco数据集介绍及下载 Microsoft COCO 数据集 COCO Dataset 数据特点 COCO 数据集的使用
- System.Web.Mvc.HttpStatusCodeResult.cs
ylbtech-System.Web.Mvc.HttpStatusCodeResult.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutra ...
- 水题两篇 Dream & Find Integer (HDU 6440/6441)
// 出自ICPC 2018网络赛C - Dream & D - Find Integer // 对大佬来讲的水题,本菜鸡尽量学会的防爆零题... // 今晚翻看vjudge昨日任务上的C题, ...