雕刻时光

just do it……nothing impossible
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

引用的剖析

Posted on 2013-12-18 22:53  huhuuu  阅读(268)  评论(0编辑  收藏  举报

《c++ primer》中这样写的:引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上是指向该对象,但用户不能访问它。

#include<iostream>

#pragma pack(1)

class data{
public:    
    double data_a;
    double &data_ra;
    data():data_ra(data_a){  //引用不能在构造函数体内初始化,要在构造函数名称右边初始化
        data_a=1.0;
    }

};

int main(){


    double a=100;
    double &ra=a;
    double *pa=&a;

    printf("%d\n",sizeof(ra));
    printf("%d\n",sizeof(&ra));
    printf("%d\n",sizeof(pa));

    data temp;
    temp.data_a=1.0;
    temp.data_ra=temp.data_a;
    printf("%d\n",sizeof(temp));
    
    getchar();
    return 0;
}

为了防止字对齐的影响,以一个字节对齐后,发现类中的引用是4个字节,也就是一个指针的大小。(节约内存考虑)

而main中引用是大小计算就是本来类型的大小,想想也是,引用本来就是一个对象的外号,指引用的时候就是指该对象。

分析为什么会是这种结果呢?背后是什么机制在起作用?

 

常量指针与指针常量我们都是知道的

常量指针如 double const *P;  //即指针不能通过*P对变量赋值,p可以不初始化

    double a=100;
    double const*  cpa2=&a;
    a=2;//可以赋值
    //*cpa2=2;//编译会报错 不能这样赋值

指针常量如 double * const p=&a;//指针所指的方向是不可改变的,且p必须初始化

现在来看看,指针常量与引用何其相似,估计在底层引用就是通过指针常量实现的,再来看

#include<iostream>
using namespace std;


int main(){
    double a=100;
    double &ra=a;
    double * const cpa=&a;

    printf("%d\n",sizeof(a));
    printf("%d\n",sizeof(ra));
    printf("%d\n",sizeof(cpa));
    printf("%d\n",sizeof(*cpa));//这里相当于printf("%d",sizeof(ra))

    getchar();
    return 0;
}

结果是 8 8 4 8

指针常量 *cpa 类似引用ra的作用了,接着就可以解释为什么在类中引用是四个字节(cpa本来就是一个指针,指针的长度就是4),但在访问它的时候编译器就自动加上了*cpa,指向了对象,于是就是它原来类型的长度了。

测试下,果然就是这么一回事。

#include<iostream>

#pragma pack(1)

class data{
public:    
    double data_a;
    double &data_ra;
    data():data_ra(data_a){  //引用不能在构造函数体内初始化,要在构造函数名称右边初始化
        data_a=1.0;
    }

};

int main(){
    data temp;
    temp.data_a=1.0;
    temp.data_ra=temp.data_a;
    printf("%d\n",sizeof(temp));
    printf("%d\n",sizeof(temp.data_a));
    printf("%d\n",sizeof(temp.data_ra));
    
    getchar();
    return 0;
}

类的长度本来是12,而main中测试的时候成为了 8 + 8,符合了假说

既然走到了这一步,为何不在看看汇编里对引用是怎么实现的。

啊哈 ,引用通过间接寻址的方式获取了相应的数据,跟指针的间接寻址是类似的!

 

引用设计的意义(既然设计出来一定是有他存在的道理的):引用的主要功能是传递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。 

但是可以想象,引用能做的事,指针也同样可以做,并且指针的功能更为强大。那么为什么还需要引用呢?答案也许是指针虽然强大但是有时太肆无忌惮了,但是引用是个被约束过的‘指针’,需要一个变量的别名的时候就只要使用引用即可。eg:比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。 

 

总结:引用是一个被包装过的指针,实现机制类似指针常量