C++基础学习4:引用
C++引用(Reference) 引用(Reference)是C++语言相对于C语言的又一个扩充,是C++常用的一个重要内容之一。类似于指针,只是在声明的时候用"&"取代了"*"。正确、灵活地使用引用,可以使程序简洁、高效。我在工作中发现,许多人使用它仅仅 是想当然,在某些微妙的场合,很容易出错,究其原由,大多因为没有搞清本源。故在本篇中我将对引用进行详细讨论,希望对大家更好地理解和使用引用起到抛砖 引玉的作用。 引用
引用是别名,在声明时必须初始化,在实际代码中主要用作函数的形参 ()&在此不是求地址运算,而是起标识作用。
()类型标识符是指目标变量的类型。
()声明引用时,必须同时对其进行初始化。
()引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
ra=; 等价于 a=;
()声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
()不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。 二、引用应用(请参见C++引用专题)
.引用作为参数
.常引用
.引用作为返回值
.引用和多态 引用可以看做是被引用对象的一个别名,在声明引用时,必须同时对其进行初始化。引用的声明方法如下:
类型标识符 &引用名 = 被引用对象 C++引用示例: int a = ;
int &b = a;
cout << a << " " << b << endl;
cout << &a << " " << &b << endl; 在本例中,变量b就是变量a的引用,程序运行结果如下: 0x64fe68 0x64fe68 从这段程序中我们可以看出变量a和变量b都是指向同一地址的,也即变量b是变量a的另一个名字,也可以理解为0x64fe68空间拥有两个名字:a和b。由于引用和原始变量都是指向同一地址的,因此通过引用也可以修改原始变量中所存储的变量值,如例2所示,最终程序运行结果是输出两个20,可见原始变量a的值已经被引用变量b修改。
[例2]通过引用修改原始变量中的值:
int a = ;
int &b = a;
b = ;
cout << a << " " << b << endl; 如果我们不希望通过引用来改变原始变量的值时,我们可以按照如下的方式声明引用:
const 类型标识符 & 引用名 = 被引用的变量名 这种引用方式成为常引用。如例3所示,我们声明b为a的常引用,之后尝试通过b来修改a变量的值,结果编译报错。虽然常引用无法修改原始变量的值,但是我们仍然可以通过原始变量自身来修改原始变量的值,如例3中,我们用a=;语句将a变量的值由10修改为20,这是没有语法问题的。 [例3]不能通过常引用来修改原始值:
复制格式化新窗口 int a = ;
const int &b = a;
b = ; //compile error
a = ; 通过例2,我们可以知道通过引用我们可以修改原始变量的值,引用的这一特性使得它用于函数传递参数或函数返回值时非常有用。 ) 函数引用参数
如果我们在声明或定义函数的时候将函数的形参指定为引用,则在调用该函数时会将实参直接传递给形参,而不是将实参的拷贝传递给形参。如此一来,如果在函数体中修改了该参数,则实参的值也会被修改。这跟函数的普通传值调用还是有区别的。 C语言函数的参数传递
按值传递,按值传递如果传递很大的数据项,赋值数据将导致较长的执行时间 C++
按引用传递,避免复制大量数据的开销,可以提高性能 引用和指针的差别
指针是个变量,可以把它再赋值成指向别处的地址
建立引用时必须进行初始化并且绝不会在关联其他不同的变量
由于指针也是变量,所以可以有指针变量的引用 int *a = nullptr;
int * &ptr = a; //表示int*的引用ptr初始化为a
int b = ;
ptr = &b; //ok, ptr是a的别名,是一个指针 void &a = ; --------- 注意这是不合法的
void只是在语法上相当于一个类型,本质上不是类型,但是没有任何一个变量或对象,其类型为void 不能遍历引用的数组
int a[] = {};
int &ra[] = a; //error不能建立一个引用类型的数组 没有引用的指针和引用的引用
int a;
int &ra = a;
int & *ptr = &ra; // error企图定义一个引用的指针 有空指针没有空引用,每一个引用都是有效的 引用的参数传递
传递引用给函数与传递指针的效果一样
用引用作为参数比使用指针更有清晰的语法 void swap(int &x, int &y); // 引用作为参数 void swap(int &x, int &y) // 函数实现几乎和原来一样
{
int temp = x;
x = y;
y = temp;
} void swap(int *x, int *y)
{
int z = *x;
*x = *y;
*y = z;
} ==============================================================
#include <iostream>
using namespace std; void foo(int val)
{
val = ;
} void bar(int &val)
{
val = ;
} void zoo(int *pval)
{
*pval = ;
} int main()
{
int a = ;
int b = ;
int c = ;
foo(a);
bar(b);
zoo(&c);
cout << a << " " << b << " " << c << endl; return ;
} ==================================================================== 引用的参数传递
使用引用作为参数和返回值给函数的意义
函数只能返回一个值。如果程序需要从函数返回两个值怎么办
解决这个问题的办法之一是引用给函数传递两个参数,然后由函数往目标中填入正确的值 函数返回值时,要生成一个值的副本。而引用返回值时,不生成值的副本,所以提高了效率。 int result = ;
int &func(int r) // 返回引用
{
result = r * r;
return result;
} 注意:如果返回不在作用域内的变量或者对象的引用就有问题了。
这与返回一个局部作用域指针的性质一样严重。 int &func(int r)
{
int result = ;
result = r * r;
return result ; // 返回局部变量的引用
} int main()
{
int &val = func(); // error返回的引用是个局部变量
return ;
} ========================================================================= 此外,我们还需要注意一个小问题。如果我们将例5中的valplus函数定义成例6中所示的形式,那么这段程序就会产生一个问题,变量b的作用域仅在这个valplus函数体内部,当函数调用完成,b变量就会被销毁。而此时我们若将b变量的值通过引用返回拷贝给变量num2的时候,有可能会出现在拷贝之前b变量已经被销毁,从而导致num2变量获取不到返回值。虽然这种情况在一些编译器中并没有发生,但是我们在设计程序的时候也是应该尽量避免这一点的。 在例4和例5中,我们就是为了避免这一点才采用的引用传递参数。普通的传值返回则不存在这样的问题,因为编译器会将返回值拷贝到临时存储空间后再去销毁b变量的。 [例6]一个可能获取不到返回值的例子: //因为b是局部变量,当程序结束以后,会自动销毁(堆栈) int & valplus(int a)
{
int b = a + ;
return b;
}
C++基础学习4:引用的更多相关文章
- 【C++基础学习】引用和指针、重载、函数、内存管理
第一部分:引用VS指针 引用的含义:变量的别名 注意:变量不能只有别名,必须有一个真实的变量与之相对应 基本数据类型的引用 对别名本身的操作和它的实体的操作是一样的 1.基本数据类型的引用 类型 &a ...
- junit基础学习之-引用spring容器的测试(7)
context 自动注入的文章链接:http://www.360doc.com/content/11/0815/09/2371584_140471325.shtml
- 如何从零基础学习VR
转载请声明转载地址:http://www.cnblogs.com/Rodolfo/,违者必究. 近期很多搞技术的朋友问我,如何步入VR的圈子?如何从零基础系统性的学习VR技术? 本人将于2017年1月 ...
- python入门到精通[三]:基础学习(2)
摘要:Python基础学习:列表.元组.字典.函数.序列化.正则.模块. 上一节学习了字符串.流程控制.文件及目录操作,这节介绍下列表.元组.字典.函数.序列化.正则.模块. 1.列表 python中 ...
- CSS零基础学习笔记.
酸菜记 之 CSS的零基础. 这篇是我自己从零基础学习CSS的笔记加理解总结归纳的,如有不对的地方,请留言指教, 学前了解: CSS中字母是不分大小写的; CSS文件可以使用在各种程序文件中(如:PH ...
- C#/.NET 基础学习
初识C# C#是微软公司发布的面向对象的.运行于.NET Framework之上的高级程序设计语言.与Java有着明显不同,借鉴Delphi的特点,与COM(组件对象模型)直接集成,是微软公司 .NE ...
- 【Bootstrap基础学习】00 序
其实这样的东西很多了,但是我就是要写. 我写这种鬼东西只是为了监督自己,如果能顺便帮一下别人就更好了. 这个系列的基础学习,不会去看实体书,主要是去看网上的资料和官网. Bootstrap就是对jQu ...
- MongoDB实战开发 【零基础学习,附完整Asp.net示例】
MongoDB实战开发 [零基础学习,附完整Asp.net示例] 阅读目录 开始 下载MongoDB,并启动它 在C#使用MongoDB 重构(简化)代码 使用MongoDB的客户端查看数据 使用Mo ...
- Oracle SQL 基础学习
oracel sql 基础学习 CREATE TABLE USERINFO ( ID ,) PRIMARY KEY, USERNAME ), USERPWD ), EMAIL ), REDATE DA ...
- ASP.NET MVC基础学习
ASP.NET MVC基础学习 传统的MVC概念 模型:组类,描述了要处理的数据以及修改和操作数据的业务规则 视图:定义应用程序用户界面的显示方式 控制器:一组类,用来处理来自用户,整个应用程序流以及 ...
随机推荐
- 发任务找不到test-unit报错
发任务的时候因找不到gem包test-unit报错, 出错行: require 'test/unit' require 'test/unit/testresult' 解决办法如下 1.通过命令查看ge ...
- [JAVA反序列化DEMO]利用RMI进行反序列化一键启动工具
功能: 命令行启动jar包,用户自定义启动RMI端口.默认内置Apache Commons Collections.只需一键启动即可测试java反序列化漏洞. 启动服务: [root@sevck_v3 ...
- Oracle——判断对象是否存在(未完工)
一.系统表: 1.User_Tables:存储用户下的所有表的信息: 2.dba_tables:存储管理员权限下的所有表的信息: 3.all_tables:存储所有表的信息. 4.all_Tab_Co ...
- UML设计九种图例
一.作为一种建模语言,UML的定义包括UML语义和UML表示法两个部分. UML语义:描述基于UML的精确元模型定义. UML表示法:定义UML符号的表示法,为开发者或开发工具使用这些图形符号和文本语 ...
- office 2010打开doc文档报错:Word 在尝试打开文件时遇到错误
今天在百度文库中下载了几个文档,下载后发现无法打开.出现以下的提示框. 那么,使用多年office的我,这点小问题当然难不倒我啦. 这个问题是由于系统安全设置所导致的 ,所有我们只需要处理这个安全设置 ...
- ThinkPad E431按F1后直接进入系统无法进入BIOS
联想的ThinkPad系列笔记本一般是按F1进如BIOS的,但是由于现在联想的笔记本多数都是预装Win 8或者更高版本的系统,所以有时候就没办法直接按F1进去BIOS.其原因是因为Win 8或者更高版 ...
- oracle系统函数(日期函数)
oracle系统函数(日期函数) 调用日期函数时,首先要明确两个概念,时间戳和日期是不同的,日期中包括年月日但不包括小时分钟秒,时间戳包括年月日小时分钟秒.在oracle中,一般情况下,函数中包含da ...
- 《Android应用性能优化》 第4章 高效使用内存
本地类型 大小 字节 boolean jboolean 8位(取决于VM) 1 byte jbyte 8位 1 char jchar 16位 2 short jshort 16位 2 int ji ...
- Android键盘属性
在主xml中android:windowSoftInputMode的属性"stateUnspecified"软键盘的状态(是否它是隐藏或可见)没有被指定.系统将选择一个合适的状态或 ...
- CentOS 6.3安装jdk(笔记整理)
1. 下载bin文件,切忌oracle上现在下载到的旧版本的jdk的bin都是网页(执行会报错,见本文最后的截图),他们需要登录oracle后才能下载,所以我这里的url是从googlecode里觅来 ...