找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2060|回复: 0
打印 上一主题 下一主题
收起左侧

[C++]笔记二十三:C++引用的本质剖析

[复制链接]
跳转到指定楼层
楼主
ID:244281 发表于 2018-4-3 20:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 tyyhmtyyhm 于 2018-4-28 10:02 编辑

第一,引用的意义
1、引用作为其它变量的别名而存,在一些场合可以代替指针
2、引用相对于指针来说具有更好的可读性和实用性。(实例可以见之前的交换两个变量的值程序
第二,引用的本质思考
思考1:引用连接的两个变量地址相同吗?
之前我们理解ba的别名,那么变量b有地址吗?还是跟a地址一样呢?
我们运行程序:
#include <iostream>
using namespace std;

int main()
{
        //const int a=10;//必须初始化

        int a=10;
        int &b=a; //b很像一个const常量

        cout<<"a="<<a<<endl;//打印a的值
        cout<<"b="<<b<<endl;//打印b的值

//a和b是同一块内存空间的“门牌号”
        cout<<"&a="<<&a<<endl;//打印a的地址
        cout<<"&b="<<&b<<endl;//打印b的地址

        system("pause");
        return 0;
}
运行结果
a=10
b=10
&a=002EF9FC
&b=002EF9FC
请按任意键继续. . .

发现ab的地址是一样的,这说明a和b是同一块内存空间的门牌号

思考2:引用占不占内存空间呢?
我们知道引用依附于一个变量,那么引用定义的变量占不占内存空间呢?
我们通过结构体来研究一下这个问题:
在结构体中定义两个引用变量,看下这两个引用变量占据多大的内存,也就是这个看下这个结构体占据多大的内存。
#include <iostream>
using namespace std;
struct Teacher
{
        char name[64];//64字节
        int age;      //4个字节
        int &a;       //多少个字节?
        int &b;
};
int main()
{
        cout<<"sizeof(Teacher)="<<sizeof(Teacher)<<endl;

        system("pause");
        return 0;
}
运行结果
sizeof(Teacher)=76
请按任意键继续. . .

通过运行我们发现结构体占了76字节,也就是说每个引用占了4个字节内存,这个和指针是一样的!指针也是占4个字节内存的!
另外,我们知道单独定义引用时,必须初始化说明引用很像一个const常量因为定义常量时 const int a = 10;必须初始化进行赋值,如果不初始化就会报错。
再加上这部分我们探讨发现引用很像一个指针,占据4字节的内存。
先对上面两部分做个小结:
我们看到引用变量b作为a的别名,二者内存地址相同,但是为什么又分别占据内存呢?援引网上的一句话:由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。
第三,引用的本质
1引用在C++中的内部实现是一个常指针;
Type &name   <----->   Type *const name
2C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同
3从使用角度,引用会让人误认为其只是一个别名,没有自己的内存空间(目标公用一个内存空间)这是C++为了实用性而做出的细节隐藏就是说C++不想让人们找到引用自己真实的地址,即使你去取引用变量b的地址,也只是目标变量a的地址。
想下指针变量具有一个地址,指针指向的空间也有一个地址,没有隐藏,很多指针指向一个对象时如果一个指针对对象进行了析构,就会使其他指针变成“野指针”,这就很危险!(该点根据自己理解,不当之处请指正!

这两个函数:
void func(int &a) //函数一
{
        a=5;
}
void func(int *const a) //函数二
{
        *a=5
}
执行函数一的时候,是C++编译器在背后在背后通过函数的方式进行了编译编译C++看到a是一个引用,就会翻译成函数二的形式再看下面的程序:
#include <iostream>
using namespace std;

int modifyA(int &a1)
{
        a1=100;
}
int modifyA2(int *a1)
{
        *a1=200;//【*实参的地址】间接修改实参的值
}
int main()
{
        int a=10;

        //指向这个函数调用的时候,我们程序员不需要取a得地址
        modifyA(a);
        cout<<"a="<<a<<endl;

        //如果是指针,需要我们程序员手工的取实参的地址
        modifyA2(&a);
        cout<<"a="<<a<<endl;

        system("pause");
        return 0;
}

当我们执行函数modifyA的时候,是C++编译器自动的取了a的地址传递给了a1执行modifyA函数调用的时候,我们就不需要再取a的地址了C++编译器帮我们取了。如果是执行modifyA2函数,使用指针,就需要传递a的地址。
第四引用小结
先来看间接赋值成立的三个条件:
1、定义两个变量(一个实参一个形参)
2建立关联,实参取地址传给形参
3*p形参去间接修改实参的值
小结:
1引用在实现上,只不过是把间接赋值成立的三个条件的后两步合二为一实参传给形参引用的时候,只不过是C++编译器帮我们程序员自动取了一个实参地址,传给了形参引用(常量指针)。
2、当我们使用引用语法时,我们不去关心编译器引用是怎么做的当我们分析奇怪的语法现象时,我们才去考虑C++编译器是怎么做的。

【C++】笔记系列均为原创,转载请注明转自微号:依法编程
更多精彩资料,请关注!


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表