c++比起c来除了多了类类型外还多出一种类型:引用。这个东西变量不象变
量,指针不象指针,我以前对它不太懂,看程序时碰到引用都稀里糊涂蒙过去。
最近把引用好好地揣摩了一番,小有收获,特公之于社区,让初学者们共享。
    引用指的是对一个对象的引用。那么什么是对象?在c++中狭义的对象指的是
用类,结构,联合等复杂数据类型来声明的变量,如 MyClass myclass,CDialo
g  mydlg,等等。广义的对象还包括用int,char,float等简单类型声明的变量
,如int a,char b等等。我在下文提到“对象”一词全指的是广义的对象。c++
的初学者们把这个广义对象的概念建立起来,对看参考书是很有帮助的,因为大
多数书上只顾用“对象”这个词,对于这个词还有广义和狭义两种概念却只字不
提。

一。引用的基本特性

首先让我们声明一个引用并使用它来初步认识引用。
例一:
       1。     int v,k,h;
       2。     int &rv=v;
       3。     rv=3;      //此时v的值也同时变成了3。
       4。     v=5;
       5。     k=rv+2;    //此时k=5+2=7。
       6。     h=12;
       7。     rv=h;
       8。     rv=20;
    第1句声明了三个对象(简单变量)。
    第2句的意思是:声明了一个引用,名字叫rv,它具有int类型,或者说它是
对int类型的引用,而且它被初始化为与int类型的对象v“绑定”在一起。此时r
v叫做对象v的引用。
    第3句把rv的值赋为3。引用的神奇之处就在这里,改变引用的值的同时也改
变了和引用所绑定在一起的对象的值。所以此时v的值也变成了3。
    第4句把v的值改为5,此时指向v的引用的值也被改成了5。所以第5句的中k的
值是5+2等于7。

上述5句说明了引用及其绑定的对象的关系:在数值上它们是联动的,改变你
也就改变了我,改变我也就改变了你。事实上,访问对象和访问对象的引用,就
是访问同一块内存区域。

第6,7,8三句说明了引用的另一个特性:从一而终。什么意思?当你在引用
的声明语句里把一个引用绑定到某个对象后,这个引用就永远只能和这个对象绑
定在一起了,没法改了。所以这也是我用了“绑定”一词的原因。而指针不一样
。当在指针的声明语句里把指针初始化为指向某个对象后,这个指针在将来如有
需要还可以改指别的对象。因此,在第7句里把rv赋值为h,并不意味着这个引用
rv被重新绑定到了h。事实上,第7句只是一条简单的赋值语句,执行完后,rv和
v的值都变成了12。第8句执行完后,rv和v的值都是20,而h保持12不变。

引用还有一个特性:声明时必须初始化,既必须指明把引用绑定到什么对象
上。大家知道指针在声明时可以先不初始化,引用不行。所以下列语句将无法通
过编译:
              int v;
              int &rv;
              rv=v;

再举一例:
例二:
          class MyClass
          {
              public:
                  int a;
                  ...
                  ...
          };
          
          MyClass  myclass;
          Myclass& cc=myclass;
          myclass.a=20;          //等价于cc.a=20
          cc.a=60;               //等价于myclass.a=60

从以上例子可以看到,无论这个对象有多复杂,使用该对象的引用或是使用
该对象本身,在语法格式上是一样的,在本质上我们都使用了内存中的同一块区
域。
    取一个引用的地址和取一个对象的地址的语法是一样的,都是用取地址操作
符"&"。例如:
          int i;
          int &ri;
          int *pi=&ri;//这句的作用和int *pi=&i是一样的。
    当然了,取一个对象的地址和取这个对象的引用的地址,所得结果是一样的

二。引用在函数参数传递中的作用

现在让我们通过函数参数的传递机制来进一步认识引用。在c++中给一个函数
传递参数有三种方法:1,传递对象本身。2,传递指向对象的指针。3,传递对象
的引用。
例三:
          class MyClass
          {
              public:
                  int a;
                  void method();
          };
          
          MyClass  myclass;
          
          void fun1(MyClass);
          void fun2(MyClass*);
          void fun3(MyClass&);

fun1(myclass);     //执行完函数调用后,myclass.a=20不变。
          fun2(&myclass);    //执行完函数调用后,myclass.a=60,改变了。

fun3(myclass);     //执行完函数调用后,myclass.a=80,改变了。

//注意fun1和fun3的实参,再次证明了:使用对象和使用对象的引用
,在语法格式上是一样的。

void fun1(MyClass mc)
          {
                mc.a=40;
                mc.method();
          }

void fun2(MyClass* mc)
          {
                mc->a=60;
                mc->method();
          }

void fun3(MyClass& mc)
          {
                mc.a=80;
                mc.method();
          }
    我们有了一个MyClass类型的对象myclass和三个函数fun1,fun2,fun3,这三个
函数分别要求以对象本身为参数;以指向对象的指针为参数;以对象的引用为参
数。

请看fun1函数,它使用对象本身作为参数,这种传递参数的方式叫传值方式
。c++将生成myclass对象的一个拷贝,把这个拷贝传递给fun1函数。在fun1函数
内部修改了mc的成员变量a,实际上是修改这个拷贝的成员变量a,丝毫影响不到
作为实参的myclass的成员变量a。
    fun2函数使用指向MyClass类型的指针作为参数。在这个函数内部修改了mc所
指向的对象的成员变量a,这实际上修改的是myclass对象的成员变量a。
    fun3使用myclass对象的引用作为参数,这叫传引用方式。在函数内部修改了
mc的成员变量a,由于前面说过,访问一个对象和访问该对象的引用,实际上是访
问同一块内存区域,因此这将直接修改myclass的成员变量a。

从fun1和fun3的函数体也可看出,使用对象和使用对象的引用,在语法格式
上是一样的。

在fun1中c++将把实参的一个拷贝传递给形参。因此如果实参占内存很大,那
么在参数传递中的系统开销将很大。而在fun2和fun3中,无论是传递实参的指针
和实参的引用,都只传递实参的地址给形参,充其量也就是四个字节,系统开销
很小。

三。返回引用的函数

引用还有一个很有用的特性:如果一个函数返回的是一个引用类型,那么该
函数可以被当作左值使用。什么是左值搞不懂先别管,只需了解:如果一个对象
或表达式可以放在赋值号的左边,那么这个对象和表达式就叫左值。
    举一个虽然无用但很说明问题的例子:
例四:
        1。     int i;
        2。     int& f1(int&);
        3。     int  f2(int);
        4。     f1(i)=3;
        5。     f2(i)=4;

int& f1(int&i)
                {
                   return i;
                }
   
                int f2(int i)
                {
                   return i;
                }
    试试编译一下,你会发现第4句是对的,第5句是错的。对这个例子而言,i的
引用被传递给了f1,然后f1把这个引用原样返回,第4句的意义和i=3是一样的。

查了查书,引用的这个特性在重载操作符时用得比较多。但是我对重载操作
符还是稀里糊涂,所以就举不出例子了。
    强调一个小问题,看看如下代码有何错误:

int &f1();
                
                f1()=5;
                ...
                ...
                int &f1()
                {
                    int i;
                    int &ri=i;
                    return ri;
                }

注意函数f1返回的引用ri是在函数体内声明的,一旦函数返回后,超出了函
数作用域,ri所指向的内存区域,即对象i所占据的内存区域就被收回了,再对这
片内存区域赋值会出错的。

四。引用的转换

前面所举的例子,引用的类型都是int类型,并且这些引用都被初始化为绑定
到int类型的对象。那么我们设想是否可以声明一个引用,它具有int类型,却被
初始化绑定到一个float类型的对象?如下列代码所示:
               float f;
               int &rv=f;
    结果证明这样的转换不能通过msvc++6.0的编译。但是引用的转换并非完全不
可能,事实上一个基类类型的引用可以被初始化绑定到派生类对象,只要满足这
两个条件:1,指定的基类是可访问的。2,转换是无二义性的。举个例子:
例五:
          class A
          {
            public:
                int a;
          };
          class B:public A
          {
           public:
                int b;
          };
          A Oa;
          B Ob;
          A& mm=Ob;
          mm.a=3;

我们有一个基类A和派生类B,并且有一个基类对象Oa和派生类对象Ob,我们
还声明了一个引用mm,它具有基类类型但被绑定到派生类对象Ob上。由于我们的
这两个类都很简单,满足那两个条件,因此这段代码是合法的。在这个例子中,
mm和派生类Ob中的基类子对象是共用一段内存单元的。所以,语句mm.a=3相当于
Ob.a=3,但是表达式mm.b却是不合法的,因为基类子对象并不包括派生类的成员

五。总结

最后把引用给总结一下:
1。对象和对象的引用在某种意义上是一个东西,访问对象和访问对象的引用其实
访问的是同一块内存区。
2。使用对象和使用对象的引用在语法格式上是一样的。
3。引用必须初始化。
4。引用在初始化中被绑定到某个对象上后,将只能永远绑定这个对象。
5。基类类型的引用可以被绑定到该基类的派生类对象,只要基类和派生类满足上
文提到的两个条件   。这时, 该引用其实是派生类对象中的基类子对象的引用

6。用传递引用的方式给函数传递一个对象的引用时,只传递了该对象的地址,系
统消耗较小。在函数体内访问    形参,实际是访问了这个作为实参的对象。
7。一个函数如果返回引用,那么函数调用表达式可以作为左值。

六。其他
1。本文中的代码在msvc++6.0中调试验证过。
2。第四节“引用的转换”中的例子:
               float f;
               int &rv=f;
   查看bc++3.1的资料,据说是合法的。此时编译器生成了一个float类型的临时
对象,引用rv被绑定到了这个临时对象上,就是说,此时rv并不是f的引用。不知
道bc++3.1里的这个特性有什么用。
3。可以在msvc++6.0里声明这样的引用:
               const int &rv=3;
   此时rv的值就是3,而且无法更改。这可能没有有什么用。因为如果我们要使
用一个符号来代表常数的话,有的是更常见的方法:
               #define rv 3
4。把第四节中的例子稍稍修改一下:
               float f;
               int &rv=(int&)f;
   这时就可以通过msvc++6.0的编译了。此时rv被绑定到了f上,rv和f共用一片
存储区。不过由于引用rv的类型是int,所以通过rv去访问这片存储区时,存储区
的内容被解释为整数;通过f去访问这片存储区时,存储区的内容被解释为实数。

http://www.cnblogs.com/wackelbh/archive/2009/12/29/1984064.html

C++的引用类型【转载】的更多相关文章

  1. 【Java讨论】引用类型赋值为null对加速垃圾回收的作用(转载)

    :有一些人认为等于null可以帮助垃圾回收机制早点发现并标识对象是垃圾.其他人则认为这没有任何帮助.是否赋值为null的问题首先在方法的内部被人提起.现在,为了更好的阐述提出的问题,我们来撰写一个Wi ...

  2. C#只能lock 引用类型的值 (转载)

    Lock:        C#只能lock 引用类型的值,如果lock一个int, bool,编译器会报错.    当一个互斥锁已被占用时,在同一线程中执行的代码仍可以获取和释放该锁.但是,在其他线程 ...

  3. 转载请注明出处: https://github.com/qiu-deqing/FE-interview

    转载请注明出处: https://github.com/qiu-deqing/FE-interview Table of Contents generated with DocToc FE-inter ...

  4. 浅析Java中的final关键字(转载)

    自http://www.cnblogs.com/dolphin0520/p/3736238.html转载 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括 ...

  5. 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱

    为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...

  6. JavaScript 深入了解基本类型和引用类型的值

    转载:https://segmentfault.com/a/1190000006752076 一个变量可以存放两种类型的值,基本类型的值(primitive values)和引用类型的值(refere ...

  7. [转载] javascript实现深度克隆

    js一般有两种不同数据类型的值: 基本类型(包括undefined,Null,boolean,String,Number),按值传递: 引用类型(包括数组,对象),按址传递,引用类型在值传递的时候是内 ...

  8. ArrayList的使用方法(转载)

    转载自: http://i.yesky.com/bbs/jsp/view.jsp?articleID=889992&forumID=150 1.什么是ArrayList    ArrayLis ...

  9. [转载] Android Bander设计与实现 - 设计篇

    本文转载自: http://blog.csdn.net/chenxiancool/article/details/17454593 摘要 Binder是Android系统进程间通信(IPC)方式之一. ...

随机推荐

  1. 史上最详细Windows版本搭建安装React Native环境配置

    说在前面的话: 感谢同事金晓冰倾情奉献本环境搭建教程 之前我们已经讲解了React Native的OS X系统的环境搭建以及配置,鉴于各大群里有很多人反应在Windows环境搭建出现各种问题,今天就特 ...

  2. HDU 1241 油田

    这道题明明很简单但不知道为什么运行结果一直错,但提交却是对的!代码真是神奇,不过我猜测可能是提上给出的数据错了,可能提上给的数据m和n后多给了一个空格或回车,但题的数据没有 #include<s ...

  3. window下安裝redis服務

    一.下载windows版本的Redis github下载地址:https://github.com/MicrosoftArchive/redis/releases/tag/win-3.2.100   ...

  4. Android高手进阶教程(十七)之---Android中Intent传递对象的两种方法(Serializable,Parcelable)!

    [转][原文] 大家好,好久不见,今天要给大家讲一下Android中Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object); ...

  5. Python中的X[:,0]和X[:,1]

    https://blog.csdn.net/csj664103736/article/details/72828584 python中 x=x[1:] 是什么意思 将x的第二位到最后一位的内容赋给x. ...

  6. Sort Colors,颜色排序

    问题描述:Given an array with n objects colored red, white or blue, sort them so that objects of the same ...

  7. mybatis报错 Error instantiating interface com.atguigu.mybatis.dao.DepartmentMapper with invalid types () or values ()

    mybatis报错 Error instantiating interface com.atguigu.mybatis.dao.DepartmentMapper with invalid types ...

  8. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [bean.xml]: Invocation of init method failed; nested exception is

    在复制xml文件进行修改的时候,我经常将不小心对原文件进行修改,而导致创建bean出错.报错如下所示: Exception sending context initialized event to l ...

  9. Go-gin CORS 跨域中间件

    原文:https://stackoverflow.com/questions/29418478/go-gin-framework-cors func CORSMiddleware() gin.Hand ...

  10. 【MAVEN】笔记

    引言 Maven是Java项目开发中的重要组成部分,了解它学习它是我们的必修课. Maven是什么? Maven是一个项目管理和综合工具.Maven是标准化的产物,使用标准的目录结构和默认构建生命周期 ...