关于“引用”的那些事儿

1.引用的概念

什么是引用,通俗地讲就是为某个目标变量起个别名(alias),对引用的操作和对目标变量的操作结果完全相同。

2.申明和使用引用”需要注意的问题

申明一个引用时,切记要对其进行初始化。引用声明完毕后,相当于目标变量有两个名称,即该目标变量的原名和引用名,不能再把该引用名作为其它变量的别名。申明一个引用,不是新定义一个变量,它只是目标变量的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统不会给引用分配存储单元。还有一点:不能建立数组的引用。

3.“引用”作为函数参数

1)传递引用给函数与传递指针的效果是一样的。被调函数的形参就称为原来主调函数中的实参变量或者对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

2)使用引用传递函数的参数,在内存中并没有生成实参的副本,它是直接对实参进行操作。而使用一般变量传递的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是是参变量的副本。如果传递的是对象,还将调用对象所属类的拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都要好。

3)使用指针作为函数的参数,虽然也能达到和使用引用一样的效果。但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行计算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参,而引用更容易使用,更清晰。

4.“常引用”的使用

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

常引用声明方式:const 类型标识符 &引用名=目标变量名;

eg1:

int a;
const int &ra=a;
ra=1; //错误
a=1; //正确 

eg2:

string foo( );
void bar(string & s);
bar(foo( ));//非法
bar("hello world"); //非法

原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。

引用型参数应该在能被定义为const的情况下,尽量定义为const 。

5.“引用”作为函数的返回值

遵循格式:

类型标识符 &函数名(形参列表及类型说明)
{

       //函数体 

}

将“引用”作为函数的返回值,可以保证在内存中不产生返回值的副本。

注意事项:

1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态,产生runtime error!

2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak

3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

4)流操作符重载返回值申明为“引用”的作用:

流操作符<<>>这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

#include <iostream>

using namespace std;

int &put(int n);
int vals[10];
int error = -1;
void main()
{
    put(0) = 10; //以put(0)函数值作为左值,等价于vals[0]=10;
    put(9) = 20; //以put(9)函数值作为左值,等价于vals[9]=20;
    cout << vals[0]<< endl;
    cout << vals[9];
}

int &put(int n)
{
    if (n >= 0 && n <= 9)
        return vals[n];
    else
    {
        cout << "subscript error";
        return error;
    }
}

运行结果:

5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

6. “引用”与多态的关系?

引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。

Class A;
Class B : Class A{...};
B b;
A& ref = b;

7. “引用与指针的区别是什么?

指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。

8. 什么时候需要引用

流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

时间: 2024-10-06 12:22:38

关于“引用”的那些事儿的相关文章

XSS与字符编码的那些事儿

目录 0x00:基本介绍 0x01:html实体编码 0x02:新增的实体编码 实体编码变异以及浏览器的某些工作原理! 0x03:javascript编码 0x04:base64编码 0x05:闲扯 0x00基本介绍 提起XSS 想到的就是插入字符字符编码与各种解析了! 这也就是各种xss编码插件跟工具出世的原因!之前不懂浏览器是如何对我们编码过的代码进行解析的时候就是一顿乱插! 各种编码 各种插 没把编码还原就算了 还原了就算运气好!后来到PKAV经过二哥和短短的调教后才算是弄清楚了一点编码与

Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名

Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名 转载自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在这之前,我们需要了解一下源代码到可执行程序之间到底发生了什么神奇而美妙的事情. 在Linux操作系统中,普遍使用ELF格

DevExpress Form那些事儿

1:设置子窗体依附父窗体 首先将父窗体的属性中  IsMdiContainer 设置为 True   , 就是将窗体设置为 MDI窗体.子窗体和父窗体都是继承自RibbonForm的. 代码如下 : 1 MainList main = new MainList(); 2 main.MdiParent = this; 3 main.Show(); 2:设置窗体加载框. 1 :  添加 SplashScreenManager  , 2 :  设置 SplashScreenManager 的 Acti

Linux系统中“动态库”和“静态库”那点事儿【转】

转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在这之前,我们需要了解一下源代码到可执行程序之间到底发生了什么神奇而美妙的事情. 在Linux操作系统中,普遍使用ELF格式作为可执行程序或者程序生成过程中的中间格式.ELF(Executable and Linking Format,可执行连接格式)是UNIX系统实验室(USL)作为应用程序二进制

JSON序列化那点事儿

JSON序列化那点事儿 序 当前主流的序列化JSON字符串主要有两种方式:JavaScriptSerializer及Json.net(Nuget标识:Newtonsoft.Json).JavaScriptSerializer是微软官方提供的一种方法,所以如果你用的是asp.net mvc,在Action中如果你返回的语句写的是”return Json(xxx);“,其实你用的就是JavaScriptSerializer方式.现在更多的人选择的是Json.net,因为它为用户提供了更加清晰地使用体

[Android]XML那些事儿-manifest属性1

在Froyo(android 2.2,API Level:8)中引入了android:installLocation.通过设置该属性可以使得开发者以及用户决定程序的安装位置. android:installLocation隶属于AndroidManifest.XML中的manifest节点.如下所示: <manifest xmlns:android="http://schemas.android.com/apk/res/android"package="string&q

最近那些事儿……

为了不耽误大家的时间,这里首先说一下下边文章中主要涉及的技术问题,如果有兴趣,大家可以一起研究一下,如果不喜欢,请绕道.写得不妥的地方,也希望大家指正. 本文主要说三个问题: 一.VS中跨工程调试,从一个工程进入另一个工程: 二.动态生成二维码和一维条形码: 三.前台页面直接访问图片流文件. 老规矩,先来点题外话.最近在园子里闲逛的时候,发现很多人都聊到了加班这个话题.其中有一哥们儿说是在京东,然后每天按时上下班,无不良记录,一个月后居然以工作不积极给开了--说到这里很多人就得有想法了,凭什么啊

SSH开发——菜鸟那些事儿

初次使用myeclipse开发,第一项目便是SSH.由于粗心,程序试运行时,错误百出,千奇百怪.下面聊聊这些事儿: 对于一个新手,我是尽可能地将自己当做一个笨的人,遇到的每一个代码都亲自去敲,认为这样能尽快熟悉项目.其中包括重复的书写的命名.可以说,这是犯忌的,这是给自己找麻烦,开发环境之所以采用了智能提示,正是为了规避无意义的重复引发错误.--相信,存在即合理. 由于myeclipse开发中因文件名书写出错(包括大小写.中英符合.减号"-"与下划线"_"),引发的

Hybird框架UI重构之路:六、前端那点事儿(Javascript)

上文回顾 :Hybird框架UI重构之路:五.前端那点事儿(HTML.CSS) 这里讲述在开发的过程中,一些JS的关键点. 换肤 对于终端的换肤,我之前一篇文章有说了我的想法. 请查看:http://www.cnblogs.com/lovesong/p/4122262.html iscroll的问题 1.使用iscroll的页面里面有表单元素,当键盘弹出再缩回后,页面拖不到最顶地方. 这个在android上总出现,使用的iscroll版本是4.2.5. 这原来是个很棘手的问题,导致了有input