第一章C++对C的扩展(Externsion)--(6)引用

6.引用(Reference)

6.1.引用的概念

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

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

6.2.规则

  • 1 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故 而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
  • 2 声明的时候必须初始化,一经声明,不可变更。
  • 3 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
  • 4 &符号前有数据类型时,是引用。其它皆为取地址。
int main()
{
    int a,b;
    int &r = a;
    int &r = b; //错误,不可更改原有的引用关系
    float &rr = b; //错误,引用类型不匹配
    cout<<&a<<&r<<endl;  //变量与引用具有相同的地址。
    int &ra = r; //可对引用更次引用,表示a变量有两个别名,分别是r和ra

}

6.3.应用

C++很少使用独立变量的引用,如果使用某一个变量,就直接使用它的原名,没有必要使用他的别名。
值作函数参数 (call by value)

void swap(int a, int b); //无法实现两数据的交换
void swap(int *p, int *q); //开辟了两个指针空间实现交换

引用作函数参数引用 (call by reference)

void swap(int &a, int &b){
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}
int main(){
    int a = 3,b = 5;
    cout<<"a = "<<a<<"b = "<<b<<endl;
    swap(a,b);
    cout<<"a = "<<a<<"b = "<<b<<endl;
    return 0;
}
c++中引入引用后,可以用引用解决的问题。避免用指针来解决。

6.4.引用提高

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

  • 1 可以定义指针的引用,但不能定义引用的引用。
int a;
int* p = &a;
int*& rp = p; // ok
int& r = a;
int&& rr = r; // error

案例:

#include <iostream>

using namespace std;

void swap(char *pa,char *pb)
{
    char *t;
    t = pa;
    pa = pb;
    pb = t;
}
void swap2(char **pa,char **pb)
{
    char *t;
    t = *pa;
    *pa = *pb;
    *pb = t;
}

void swap3(char * &pa,char *&pb)
{
    char *t;
    t = pa;
    pa = pb;
    pb = t;
}


int main()
{
    char *pa = "china";
    char *pb =  "america";

    cout<<"pa "<<pa<<endl;
    cout<<"pb "<<pb<<endl;
//    swap(pa,pb);
//    swap2(&pa,&pb);
    swap3(pa,pb);
    cout<<"pa "<<pa<<endl;
    cout<<"pb "<<pb<<endl;
    return 0;
}
  • 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引用。实际上,const int a=1; int &b=a;这种写法是不合法的,编译不过。
    • const引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是const引用与普通引用最大的区别。const int &a=2;是合法的。double x=3.14; const int &b=a;也是合法的。
      常引用原理:
      const引用的目的是,禁止通过修改引用值来改变被引用的对象。const引用的初始化特性较为微妙,可通过如下代码说明:
double val = 3.14;
const int &ref = val; // int  const &   int & const ?? 
double & ref2 = val;
cout<<ref<<" "<<ref2<<endl;
val = 4.14;
cout<<ref<<" "<<ref2<<endl;

上述输出结果为3 3.14和3 4.14。因为ref是const的,在初始化的过程中已经给定值,不允许修改。而被引用的对象是val,是非const的,所以val的修改并未影响ref的值,而ref2的值发生了相应的改变。
那么,为什么非const的引用不能使用相关类型初始化呢?实际上,const引用使用相关类型对象初始化时发生了如下过程:

int temp = val;
const int &ref = temp;

如果ref不是const的,那么改变ref值,修改的是temp,而不是val。期望对ref的赋值会修改val的程序员会发现val实际并未修改。

int i=5;
const int & ref = i+5;

//此时产生了与表达式等值的无名的临时变量,
//此时的引用是对无名的临时变量的引用。故不能更改。

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

6.5.引用的本质浅析

6.5.1.大小与不可再引用

引用的本质是指针,是个什么样指针呢?可以通过两方面来探究,初始化方式和大小。

struct TypeP
{
    char *p;
};
struct TypeC
{
    char c;
};
struct TypeR
{
    char& r;  //把引用单列出来,不与具体的对像发生关系
};


int main()
{

//    int a;
//    int &ra = &a;
//    const int rb;  //const 类型必须要初始化。


 printf("%d %d %d\n",sizeof(TypeP),sizeof(TypeC),sizeof(TypeR));
    return 0;
}

结论:
引用的本质是,是对常指针type * const p的再次包装。
char &rc == *pc double &rd == *pd

6.5.2.反汇编对比指针和引用

原程序

#include <iostream>
using namespace std;
void Swap(int *p, int *q)
{
    int t = *p;
    *p = *q;
    *q = t;
}
void Swap(int &p, int &q)
{
    int t = p;
    p = q;
    q = t;
}
int main()
{
    int a = 3; int b =5;
    Swap(a,b);
    Swap(&a,&b);
    return 0;
}

汇编程序

[1] {
                 55                       push   %ebp
<+0x0001>        89 e5                    mov    %esp,%ebp
<+0x0003>        83 e4 f0                 and    $0xfffffff0,%esp
<+0x0006>        83 ec 20                 sub    $0x20,%esp
<+0x0009>        e8 ce 0a 00 00           call   0x402130 <__main>
[1]     int a = 3; int b =5;
<+0x000e>        c7 44 24 1c 03 00 00 00  movl   $0x3,0x1c(%esp)
<+0x0016>        c7 44 24 18 05 00 00 00  movl   $0x5,0x18(%esp)
[1]     Swap(a,b);
<+0x001e>        8d 44 24 18              lea    0x18(%esp),%eax
<+0x0022>        89 44 24 04              mov    %eax,0x4(%esp)
<+0x0026>        8d 44 24 1c              lea    0x1c(%esp),%eax
<+0x002a>        89 04 24                 mov    %eax,(%esp)
<+0x002d>        e8 ac ff ff ff           call   0x401632 <Swap(int&, int&)>
[1]     Swap(&a,&b);
<+0x0032>        8d 44 24 18              lea    0x18(%esp),%eax
<+0x0036>        89 44 24 04              mov    %eax,0x4(%esp)
<+0x003a>        8d 44 24 1c              lea    0x1c(%esp),%eax
<+0x003e>        89 04 24                 mov    %eax,(%esp)
<+0x0041>        e8 76 ff ff ff           call   0x401610 <Swap(int*, int*)>
[1]     return 0;
<+0x0046>        b8 00 00 00 00           mov    $0x0,%eax
[1] }
<+0x004b>        c9                       leave
<+0x004c>        c3                       ret

10x401632 Swap(int&, int&)

     12 [1] {
0x401632                   55        push   %ebp
0x401633  <+0x0001>        89 e5     mov    %esp,%ebp
0x401635  <+0x0003>        83 ec 10  sub    $0x10,%esp
        13 [1]      int t = p;
0x401638  <+0x0006>        8b 45 08  mov    0x8(%ebp),%eax
0x40163b  <+0x0009>        8b 00     mov    (%eax),%eax
0x40163d  <+0x000b>        89 45 fc  mov    %eax,-0x4(%ebp)
        14 [1]      p = q;
0x401640  <+0x000e>        8b 45 0c  mov    0xc(%ebp),%eax
0x401643  <+0x0011>        8b 10     mov    (%eax),%edx
0x401645  <+0x0013>        8b 45 08  mov    0x8(%ebp),%eax
0x401648  <+0x0016>        89 10     mov    %edx,(%eax)
        15 [1]      q = t;
0x40164a  <+0x0018>        8b 45 0c  mov    0xc(%ebp),%eax
0x40164d  <+0x001b>        8b 55 fc  mov    -0x4(%ebp),%edx
0x401650  <+0x001e>        89 10     mov    %edx,(%eax)
        16 [1]  }
0x401652  <+0x0020>        c9        leave
0x401653  <+0x0021>        c3        ret

0x401610 Swap(int*, int*)

        6 [1]   {
0x401610                   55        push   %ebp
0x401611  <+0x0001>        89 e5     mov    %esp,%ebp
0x401613  <+0x0003>        83 ec 10  sub    $0x10,%esp
        7 [1]       int t = *p;
0x401616  <+0x0006>        8b 45 08  mov    0x8(%ebp),%eax
0x401619  <+0x0009>        8b 00     mov    (%eax),%eax
0x40161b  <+0x000b>        89 45 fc  mov    %eax,-0x4(%ebp)
        8 [1]       *p = *q;
0x40161e  <+0x000e>        8b 45 0c  mov    0xc(%ebp),%eax
0x401621  <+0x0011>        8b 10     mov    (%eax),%edx
0x401623  <+0x0013>        8b 45 08  mov    0x8(%ebp),%eax
0x401626  <+0x0016>        89 10     mov    %edx,(%eax)
        9 [1]       *q = t;
0x401628  <+0x0018>        8b 45 0c  mov    0xc(%ebp),%eax
0x40162b  <+0x001b>        8b 55 fc  mov    -0x4(%ebp),%edx
0x40162e  <+0x001e>        89 10     mov    %edx,(%eax)
        10 [1]  }
0x401630  <+0x0020>        c9        leave
0x401631  <+0x0021>        c3        ret

对比结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

developer_wgl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值