引用

概念与创建引用

变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用,是为己有变量起一个别名。
声明如下:

int main() 
{
    int a;
    int &b = a; 
}

规则

  1. 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。

  2. 声明的时候必须初始化,一经声明,不可变更。

  3. 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。

    int main() 
    {
         int a,b; int &r = a;
         int &r = b; //错误,不可更改原有的引用关系
         float &rr = b; //错误,引用类型不匹配
         cout<<&a<<&r<<endl; //变量与引用具有相同的地址。
         int &ra = r; //可对引用更次引用,表示 a 变量有两个别名,分别是 r 和 ra
    }
    

应用

引用用于函数参数

以实现两个数据的交换为例:

void swapv(int a, int b);   //传值无法实现数据的交换

void swapp(int *a, int *b); //传指针实现数据交换

void swapr(int &a, int &b); //传引用也可以实现数据交换,避免用指针来解决

作为函数参数:

void swapv(int a, int b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
void swapp(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

void swapr(int &a, int &b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}

引用用于结构

引用非常适合用于结构和类,确实,引入引用主要也是为了用于这些类型的,而不是基本的内置类型

假设有如下结构定义:

struct people
{
    string name;
    int age;
    float height;
};

则可以这样编写函数原型,将指向结构的引用作为函数参数:

void set_age(people &p);

如果不希望修改函数传入的结构,也可以使用const(在引用补充部分会说):

void display(const people &p);

函数返回引用:

people & add_age(people &p1,const people &p2);
p3=add_age(p1,p2);

这里可以理解为:如果add_age返回的是一个结构体,而不是指向结构的引用,将把整个结构复制到一个临时位置,再将这个拷贝复制给p3,但当返回值为引用时,会直接将p1(p2)复制到p3,效率更高。

返回引用时需要注意的问题:应避免返回函数终止时不再存在的内存单元引用,类似于下面的代码:

const people &clone(people &p1)
{
    people p3;
    p3=p1;
    return p3;
}
//函数返回了一个临时变量的引用,函数退出后,它将不再存在

引用用于类对象

以拼接字符串为例:

//函数申明
string version1(const string &s1, const string &s2);
const string &version2(string &s1, const string &s2);

//函数实现
string version1(const string &s1, const string &s2)
{
    string temp;//定义临时变量
    temp = s2 + s1 + s2;
    return temp;
}

const string &version2(string &s1, const string &s2)
{
    s1 = s2 + s1 + s2; //修改了S1的值
    return s1;
}

引用补充

引用的本质是指针,C++对裸露的内存地址(指针)作了一次包装。又取得的指针的优良特
性。所以再对引用取地址,建立引用的指针没有意义。

  1. 可以定义指针的引用,但不能定义引用的引用。

    int a;
    int* p = &a;
    int*& rp = p; // ok int& r = a;
    int&& rr = r; // error
    
  2. 可以定义指针的指针(二级指针),但不能定义引用的指针。

    int a;
    int* p = &a;
    int** pp = &p; // ok
    int& r = a;
    int&* pr = &r; // error
    
  3. 可以定义指针数组,但不能定义引用数组,可以定义数组引用。

    int a, b, c;
    int* parr[] = {&a, &b, &c}; // ok int& rarr[] = {a, b, c}; // error
    int arr[] = {1, 2, 3};
    int (&rarr)[3] = arr; // ok 的
    
  4. 常引用

    • const 对象的引用必须是 const 的,将普通引用绑定到 const 对象是不合法的。 这 个原因比较简单。既然对象是 const 的,表示不能被修改,引用当然也不能修改,必须使 用 const 引用。

      const int a=10;
      int &ra=a;//error
      const int &rra=a;//ok
      
    • const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是 const 引用与普通引用最大的区别。

      double refcube(const double &ra)
      {
          return ra*ra*ra;
      }
      
      //考虑如下代码:
      double side = 3.0;
      double *pd = &side;
      double &rd = side;
      long edge = 5L;
      double lens[4] = {3.0, 4.0, 5.0, 6.0};
      double c1 = refcube(side);        //ra is side;
      double c2 = refcube(lens[2]);     //ra is lens[2];
      double c3 = refcube(rd);          //ra is rd is side;
      double c4 = refcube(*pd);         //ra is *pd is side;
      double c5 = refcube(edge);        //ra is tamporary variable;
      double c6 = refcube(7.0);         //ra is tamporary variable;
      double c7 = refcube(side + 10.0); //ra is tamporary variable;
      
      说明:如果函数调用的参数出现以下两种情况:
        * 为常量或者表达式(如c6和c7)
        * 参数与const引用的参数类型不匹配
      那么C++将创建正确的临时变量,将函数调用的参数(实参)的值传递给该临时变量,并让参数(形参)来引用该变量。
      
    • 应尽可能使用const

      • 使用 const 可以避免无意修改数据的编程错误。
      • 使用 const 可以处理 const 和非 const 实参。否则将只能接受非 const 数据。
      • 使用 const 引用,可使函数能够正确的生成并使用临时变量(如果实参与引用参
        数不匹配,就会生成临时变量)。

何时使用引用

使用引用参数的原因主要有两个

  • 能够修改调用函数中的数据对象

  • 通过传递引用而不是整个数据对象,提高程序的运行速度

    使用引用的一些指导原则如下表:

内置数据类型 数组 结构 类对象
对于使用传递的值而不做修改的函数 值传递 const指针 const指针或const引用 const引用
对于修改调用函数中数据对象的函数 指针 指针 指针或引用 引用

关于左值,右值,左值引用,右值引用,引用类型的重载

  1. 左值,右值

    在C++11中所有的值必属于左值、右值两者之一,右值又可以细分为纯右值、将亡值。在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。举个例子,int a = b+c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;表达式b+c、函数int func()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b+c)这样的操作则不会通过编译。1.

  2. 左值引用,右值引用

    左值引用就是对一个左值进行引用的类型。右值引用就是对一个右值进行引用的类型,事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。

    右值引用左值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。

    左值引用通常也不能绑定到右值,但常量左值引用(const)是个“万能”的引用类型。它可以接受非常量左值、常量左值、右值对其进行初始化。不过常量左值所引用的右值在它的“余生”中只能是只读的。相对地,非常量左值只能接受非常量左值对其进行初始化。

int &a = 2;       # 左值引用绑定到右值,编译失败

int b = 2;        # 非常量左值
const int &c = b; # 常量左值引用绑定到非常量左值,编译通过
const int d = 2;  # 常量左值
const int &e = c; # 常量左值引用绑定到常量左值,编译通过
const int &b =2;  # 常量左值引用绑定到右值,编程通过

右值值引用通常不能绑定到任何的左值,要想绑定一个左值到右值引用,通常需要std::move()将左值强制转换为右值,例如:

int a;
int &&r1 = c;             # 编译失败
int &&r2 = std::move(a);  # 编译通过

下表列出了在C++11中各种引用类型可以引用的值的类型。


image.png

3.引用类型的重载

由于类设计和STL经常使用引用参数,因此知道不同引用类型的重载很有用,请看下面三个原型:

void sink(double &s1);
void sink(const double &s2);
void sink(double && s3);
说明:
左值引用参数r1与左值匹配
常量左值引用参数与左值,常量左值,右值匹配
右值引用参数与右值匹配
举例说明:
double x=3.333;
const double y=22.0;
sink(x);//匹配void sink(double &s1);
sink(y);//匹配void sink(const double &s2);
sink(x+y);//匹配void sink(double && s3);
注:当没有定义void sink(double && s3)时,sink(x+y)将匹配void sink(const double &s2);

参考
C++11 左值、右值、右值引用详解
C++ Primer Plus

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