c++左值引用和右值引用 2024-02-25

C++工程代码中经常会看见这样的函数入参:

void fun(int & a, int* b){
  ...
  return;
}

上面的函数使用了引用作为函数的入参;

左值引用和右值引用

有时候还会见到更加奇怪的变量定义:

int a = 10;
int &b = a;
int &&c = 10; 

上面代码内b就是左值引用了,而c就是右值引用;

c++左值与右值

int a = 10;

c++代码原本就有左值和右值之分,其中上面代码第一行变量a就是左值,而数字10就是右值;
一般来说左值在内存中是有分配空间的,而右值很有可能因为只是中间量,可能只存在cpu某个寄存器内而没有具体内存地址;
简单来说,在‘=’左边的是左值,而在‘=’右边的就是右值。

回到引用的说明,引用是变量的别名,它使用不同名称“指向”了内存的某一具体地址;显然根据上面左值右值的说明可以发现,如果某个变量没有在内存内(右值),而我们又想对他定义引用,怎么办?这就有了右引用的用武之地;

int a = 10;
int &&c = 10; 

正常情况下上面的代码中,第一行的右值“10”的生存周期只在当行有效。在执行第二行时,对应的寄存器或者内存会释放掉。程序会申请一个内存地址叫做‘a’,地址内存放10。
对于第二行,右值“10”在定义c后,会转化为左值被c引用,并且在执行完当前行后,“10”继续存在在内存中(有地址被c记录)直到c被释放,显然c解决了右值生存周期的问题。

那为什么需要右值引用这种看似很无聊的操作呢:
减少不必要的拷贝,同时完成必要的数据转换!

来个例子

#include <iostream>  
using namespace std;  

int fun(int &v1,  const int &v2){
    
    v1 = v2 + 1;
    cout << "-------入参引用--------" << endl;
    cout << "   v1  =" << v1 <<"    "<<"    &v1 = "<<&v1<< endl;
    cout << "   v2  =" << v2 <<"    "<<"    &v2 = "<<&v2<< endl;
    cout << "-----------------------" << endl;
    return 0;
}

int main()  
{   
    int v1 = 1;
    int v2 = 1;
    cout << "-------入参定义--------" << endl;
    cout << "   v1  =" << v1 <<"    "<<"    &v1 = "<<&v1<< endl;  
    cout << "   v2  =" << v2 <<"    "<<"    &v2 = "<<&v2<< endl;  
    cout << "-----------------------" << endl;
    fun(v1, v2);
    
    return 0;  
}  

-------入参定义--------
   v1  =1        &v1 = 0x7fff062557dc
   v2  =1        &v2 = 0x7fff062557d8
-----------------------
-------入参引用--------
   v1  =2        &v1 = 0x7fff062557dc
   v2  =1        &v2 = 0x7fff062557d8
 -----------------------

const 引用的特殊性

上面例子中const修饰函数入参,除了不让修改和普通入参引用似乎没什么区别。
如果把变量v2修改为float,我们发现编译并不会报错,修改后的代码如下:

#include <iostream>  
using namespace std;  
int fun(int &v1,  const int &v2){
   
   v1 = v2 + 1;
   cout << "-------入参引用--------" << endl;
   cout << "   v1  =" << v1 <<"    "<<"    &v1 = "<<&v1<< endl;
   cout << "   v2  =" << v2 <<"    "<<"    &v2 = "<<&v2<< endl;
   cout << "-----------------------" << endl;
   return 0;
}

int main()  
{  

   int v1 = 1;
   float v2 = 1;
   cout << "-------入参定义--------" << endl;
   cout << "   v1  =" << v1 <<"    "<<"    &v1 = "<<&v1<< endl;  
   cout << "   v2  =" << v2 <<"    "<<"    &v2 = "<<&v2<< endl;  
   cout << "-----------------------" << endl;
   fun(v1, v2);
   fun(v1,2);
   return 0;  
}  

代码执行结果如下:

-------入参定义--------
   v1  =1        &v1 = 0x7fff7782a864
   v2  =1        &v2 = 0x7fff7782a860
-----------------------
-------入参引用--------
   v1  =2        &v1 = 0x7fff7782a864
   v2  =1        &v2 = 0x7fff7782a868
-----------------------
-------入参引用--------
   v1  =3        &v1 = 0x7fff7782a864
   v2  =2        &v2 = 0x7fff7782a86c
-----------------------

发现编译器通过const int & v2 自动帮我们完成了数据类型转换。(个人理解是const关键字说明该引用不会影响函数外的变量且会申请新的内存地址变为左值,所以自动完成内存转换不会有任何风险)。
这里可以尝试修改v1的类型为float 或者在fun函数的第一个入参为右值均无法编译通过。

当然其实在复杂的工程代码上,有些更加变态的代码会有如下需求:

int fun(const T &a); // 函数申明
/**/
int b1 = fun(new T);
T t; 
int b2 = fun(t);

其中的差别需要积累经验慢慢体会。

其他

写法上const关键字可以放在任何位置;常引用随便绑;

int &a = 2;       // 左值引用绑定到右值,编译失败
int &&a = 2;       // 右值引用绑定到右值,编译ok
int b = 2;        // 非常量左值
const int &c = b; // 常量左值引用绑定到非常量左值,编译ok
const int d = 2;  // 常量左值
const int &e = c; // 常量左值引用绑定到常量左值,编译ok
const int &f =2;  // 常量左值引用绑定到右值,编程ok
int const &g =2;  // 和上一句等价,编程ok

右值引用不能直接绑定到左值,如果要绑定可以使用std::move()函数;

int a;
int &&r1 = a;    // 编译ok
int &&r2 = std::move(a);  //编译ok
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容