深入分析C++引用

      关于引用和指针的差别的文章非常多非常多,可是总是找不到他们的根本差别,偶然在codeproject上看到这篇文章,认为讲的挺好的,

所以翻译了下,希望对大家有帮助。

原文地址: http://www.codeproject.com/KB/cpp/References_in_c__.aspx

 

引言

      我选择写 C++ 中的引用是由于我感觉大多数人误解了引用。而我之所以有这个感受是由于我主持过非常多 C++ 的面试,而且我非常少从面试者中得到关于 C++ 引用的正确答案。

       那么 c++ 中引用究竟意味这什么呢?通常一个引用让人想到是一个引用的变量的别名,而我讨厌将 c++ 中引用定义为变量的别名。这篇文章中,我将尽量解释清楚, c++ 中根本就没有什么叫做别名的东东。

 

背景

c/c++ 中,訪问一个变量仅仅能通过两种方式被訪问,传递,或者查询。这两种方式是:

1. 通过值 訪问 / 传递变量

2. 通过地址 訪问 / 传递变量 这样的方法就是指针

 

       除此之外没有第三种訪问和传递变量值的方法。引用变量也就是个指针变量,它也拥有内存空间。最关键的是引用是一种会被编译器自己主动解引用的指针。非常难相信么?让我们来看看吧。。。

 

以下是一段使用引用的简单 c++ 代码

 #include <iostream.h> int main() { int i = 10; // A simple integer variable int &j = i; // A Reference to the variable i j++; // Incrementing j will increment both i and j. // check by printing values of i and j cout<< i << j <<endl; // should print 11 11 // Now try to print the address of both variables i and j cout<< &i << &j <<endl; // surprisingly both print the same address and make us feel that they are // alias to the same memory location. // In example below we will see what is the reality return 0; } 

 

引用事实上就是 c++ 中的常量指针。表达式   int &i = j; 将会被编译器转化成 int *const i = &j; 而引用之所以要初始化是由于 const 类型变量必须初始化,这个指针也必须有所指。以下我们再次聚焦到上面这段代码,并使用编译器的那套语法将引用替换掉。

 

#include <iostream.h> int main() { int i = 10; // A simple integer variable int *const j = &i; // A Reference to the variable i (*j)++; // Incrementing j. Since reference variables are // automatically dereferenced by compiler // check by printing values of i and j cout<< i << *j <<endl; // should print 11 11 // A * is appended before j because it used to be reference variable // and it should get automatically dereferenced. return 0; }

 

    读者一定非常奇怪为什么我上面这段代码会跳过打印地址这步。这里须要一些解释。由于引用变量时会被编译器自己主动解引用的,那么一个诸如   cout << &j << endl; 的语句,编译器就会将其转化成语句   cout << &*j << endl;   如今 &* 会相互抵消,这句话变的毫无意义,而 cout 打印的 j 值就是 i 的地址,由于其定义语句为 int *const j = &i;

 

      所以语句 cout << &i << &j << endl; 变成了 cout << &i << &*j << endl; 这两种情况都是打印输出 i 的地址。这就是当我们打印普通变量和引用变量的时候会输出同样地址的原因。

 

      以下给出一段复杂一些的代码,来看看引用在级联 (cascading) 中是怎样运作的。

 

#include <iostream.h> int main() { int i = 10; // A Simple Integer variable int &j = i; // A Reference to the variable // Now we can also create a reference to reference variable. int &k = j; // A reference to a reference variable // Similarly we can also create another reference to the reference variable k int &l = k; // A reference to a reference to a reference variable. // Now if we increment any one of them the effect will be visible on all the // variables. // First print original values // The print should be 10,10,10,10 cout<< i << "," << j << "," << k << "," << l <<endl; // increment variable j j++; // The print should be 11,11,11,11 cout<< i << "," << j << "," << k << "," << l <<endl; // increment variable k k++; // The print should be 12,12,12,12 cout<< i << "," << j << "," << k << "," << l <<endl; // increment variable l l++; // The print should be 13,13,13,13 cout<< i << "," << j << "," << k << "," << l <<endl; return 0; }

 

以下这段代码是将上面代码中的引用替换之后代码,也就是说明我们不依赖编译器的自己主动替换功能,手动进行替换也能达到同样的目标。

 

#include <iostream.h> int main() { int i = 10; // A Simple Integer variable int *const j = &i; // A Reference to the variable // The variable j will hold the address of i // Now we can also create a reference to reference variable. int *const k = &*j; // A reference to a reference variable // The variable k will also hold the address of i because j // is a reference variable and // it gets auto dereferenced. After & and * cancels each other // k will hold the value of // j which it nothing but address of i // Similarly we can also create another reference to the reference variable k int *const l = &*k; // A reference to a reference to a reference variable. // The variable l will also hold address of i because k holds address of i after // & and * cancels each other. // so we have seen that all the reference variable will actually holds the same // variable address. // Now if we increment any one of them the effect will be visible on all the // variables. // First print original values. The reference variables will have * prefixed because // these variables gets automatically dereferenced. // The print should be 10,10,10,10 cout<< i << "," << *j << "," << *k << "," << *l <<endl; // increment variable j (*j)++; // The print should be 11,11,11,11 cout<< i << "," << *j << "," << *k << "," << *l <<endl; // increment variable k (*k)++; // The print should be 12,12,12,12 cout<< i << "," << *j << "," << *k << "," << *l <<endl; // increment variable l (*l)++; // The print should be 13,13,13,13 cout << i << "," << *j << "," << *k << "," << *l <<endl; return 0; }

 

         我们通过以下代码能够证明 c++ 的引用不是神马别名,它也会占用内存空间的。

#include <iostream.h> class Test { int &i; // int *const i; int &j; // int *const j; int &k; // int *const k; }; int main() { // This will print 12 i.e. size of 3 pointers cout<< "size of class Test = " << sizeof(class Test) <<endl; return 0; }

 

 

结论

我希望这篇文章能把 c++ 引用的全部东东都解释清楚,然而我要指出的是 c++ 标准并没有解释编译器怎样实现引用的行为。所以实现取决于编译器,而大多数情况下就是将事实上现为一个 const 指针。

 

 

引用支持 c++ 虚函数机制的代码

 

#include <iostream.h> class A { public: virtual void print() { cout<<"A.."<<endl; } }; class B : public A { public: virtual void print() { cout<<"B.."<<endl; } }; class C : public B { public: virtual void print() { cout<<"C.."<<endl; } }; int main() { C c1; A &a1 = c1; a1.print(); // prints C A a2 = c1; a2.print(); // prints A return 0; }

 

 

上述代码使用引用支持虚函数机制。假设引用不过一个别名,那怎样实现虚函数机制,而虚函数机制所须要的动态信息只能通过指针才干实现,所以更加说明引用事实上就是一个 const 指针。

转载于:https://www.cnblogs.com/bhlsheji/p/4232061.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/420285.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql8窗口函数

mysql window函数mysql 开窗函数demo&practice分组求和2. 和MySQL其它函数搭配mysql 开窗函数demo&practice 注意: mysql8 才有开窗函数, mysql5.7以下不要阅读。分组求和 create table sales (id int PRIMARY KEY auto_increment,city varchar(15),county varchar(1…

前端学习(1341):mongoose验证规则延伸

const mongoose require(mongoose); mongoose.connect(mongodb://localhost/playground, { useUnifiedTopology: true }).then(() > console.log(数据库连接成功)).catch(err > console.log(err, 数据库连接失败))//创建集合规则 const postSchema new mongoose.Schema…

艾创机器人_世界教育机器人大赛 2019赛季世界锦标赛落幕曲靖代表队获多个奖项...

来源&#xff1a;曲靖日报-曲靖新闻网本报讯12月14日,世界教育机器人大赛(WER)2019赛季世界锦标赛在上海隆重开赛。来自中国、美国、英国、日本、菲律宾、墨西哥等30多个国家和地区的1万余名选手在大赛中一决高低。刚刚结束的第六届曲靖市青少年机器人竞赛余温未散,从比赛中脱颖…

javascript 学习笔记

一&#xff1a; js数据类型 js有两类数据类型&#xff0c;1&#xff1a;原始类型&#xff1b;2对象类型。 原始类型包括5中&#xff1a;数字&#xff0c;字符串&#xff0c;布尔&#xff0c;nil&#xff0c;undifined。 nil和undefined分属不同的类型&#xff0c;而此两种类型比…

MySQL8权限,角色

角色创建角色给角色赋予权限删除权限给用户赋予角色激活角色撤销用户权限创建角色 mysql> create role boss; Query OK, 0 rows affected (0.01 sec)mysql> create role manager; Query OK, 0 rows affected (0.01 sec)给角色赋予权限 manager角色拥有查询sales表的权限…

前端学习(1342):mongoose验证规则拿到错误信息

const mongoose require(mongoose); mongoose.connect(mongodb://localhost/playground, { useUnifiedTopology: true }).then(() > console.log(数据库连接成功)).catch(err > console.log(err, 数据库连接失败))//创建集合规则 const postSchema new mongoose.Schema…

单片机人流统计装置的程序_单片机其实不难

对于大学读电子方面专业的同学们&#xff0c;肯定知道有这么一个神奇的元器件&#xff0c;它枯燥难懂&#xff0c;但也十分吸引人&#xff0c;它就是我们今天要讲的元器件--单片机单片机作为工业控制领域里面最核心的部件&#xff0c;它存在于每一台机器&#xff0c;小到扫地机…

数据库索引的作用和长处缺点

为什么要创建索引呢&#xff1f;这是由于&#xff0c;创建索引能够大大提高系统的性能。 第一&#xff0c;通过创建唯一性索引&#xff0c;能够保证数据库表中每一行数据的唯一性。 第二&#xff0c;能够大大加快 数据的检索速度&#xff0c;这也是创建索引的最基本的原因。 第…

MySQL索引篇

index存储引擎索引InnoDB中的索引MyISAM索引存储引擎 以前一直认为关系型数据库中的索引不重要&#xff0c;知道最近学了MySQL高级篇&#xff0c;才发现&#xff0c;对MySQL一知半解。都是听人泛泛而谈。 首先MySQL服务器是怎么存数据的&#xff0c;怎么取到的&#xff0c;内…

sublime加入input函数_【挑战自学Python编程】第八天:while循环以及input()函数

摘要01 while循环02 input函数03 终端04 使用while循环与input()函数01 while循环在正式讲Python中的while前&#xff0c;希望大家先关注单词一下while&#xff0c;翻译为中文意思是&#xff1a;当。&#xff08;这里我们只需要这一种意思即可&#xff09;下面我们开始看while循…

文件循环读取_一个案例轻松认识Python文件处理提取文件中的数字

1、文件打开使用 open() 函数打开文件。它需要两个参数&#xff0c;第一个参数是文件路径或文件名&#xff0c;第二个是文件的打开模式。模式通常是下面这样的&#xff1a;"r"&#xff0c;以只读模式打开&#xff0c;你只能读取文件但不能编辑/删除文件的任何内容&qu…

B/S架构

程序架构 tomcat目录结构&#xff1a; bin:存放启动和关闭tomcat的脚本文件&#xff1b; conf:存放配置文件 lib:存放所需的jar文件 webapps:存放发布的web程序 work:存入tomcat工作时产生的文件 1.tomcat配置端口&#xff0c;当默认的8080端口被占用时修改8080端口的位置在tom…

前端学习(1344):用户的增删改查操作1

const http require(http); const mongoose require(mongoose);//数据库连接 mongoose.connect(mongodb://localhost/playground, { useUnifiedTopology: true }).then(() > console.log(数据库连接成功)).catch(() > console.log(数据库连接失败));const userSchema …

索引常用注意事项

索引1. 索引怎么建好&#xff1f;2. 索引容易失效的场景3. 连接查询索引优化4. order by&#xff0c;group by5. 覆盖索引6. 索引下推1. 索引怎么建好&#xff1f; 单表 主键必须唯一&#xff0c;且单调递增有唯一键的&#xff0c;尽量建立唯一键where条件用得比较多的字段查询…

EntityFramework的安装

关于EntityFramework在vs2012无法引用的问题 这段时间学习MVC&#xff0c;发现一个问题&#xff0c;我公司的电脑可以直接引用EntityFrameWork这个命名空间&#xff0c;但我家里面的电脑就不能直接引用&#xff0c;刚开始以为是我电脑配置问题&#xff0c;后重装电脑&#xff0…

毫米波雷达_最新的7个毫米波雷达应用案例

毫米波雷达传感器如何做到"全天候"&#xff1f;毫米波雷达使用的技术是毫米波(millimeterwave)&#xff0c;通常缩写为MMW&#xff0c;波长为1&#xff5e;10毫米&#xff0c;频率为30~300GHz的电磁波。根据波的传播理论&#xff0c;频率越高&#xff0c;波长越短&am…

前端学习(1345):用户的增删改查操作2

//创建http连接 const http require(http); //创建服务器 const app http.createServer(); //第三方模块导入 const mongoose require(mongoose); //获取连接 const url require(url); //数据库连接地址 mongoose.connect(mongodb://localhost/playground, { useUnifiedTop…

太阳光是平行光吗_“彩虹的形成是因为光的色散和光沿直线传播是一回事吗?”...

-1感谢某不愿透露姓名的高中同学提供支持。0请先解释一下你的这个问题提法可能的歧义&#xff1a;究竟是“是因为”后面的词语组成一个整体&#xff0c;还是“和”前面的词语组成一个整体呢&#xff1f;不讲清楚的话&#xff0c;答案会有一些差距。1“彩虹的形成是因为光的色散…

力扣刷题【20,21,26,27,35】

-20 有效的括号21 合并两个有序链表26 删除数组中的重复项27. 移除元素35. 搜索插入位置20 有效的括号 使用replace一直替换 package top.lel.lc.easy.valid_parentheses;import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.…