C++的引用类型【转载】

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

时间: 2024-10-31 08:34:47

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

引用类型(转载)

引用类型 引用类型 1)sun公司或其他的程序员编写的类型,可以用来简化我们的开发,定义的新的数据类型,成为引用类型 2)什么是类? 代表就是一类事物,抽象的概念,比如人类,植物类 3)什么是对象? 代表就是一个具体,实际存在的事物,比如门口的自行车 4)java中的类,就是引用类型 eg: class Perosn { //      属性 int age; String name; //      方法:行为 } 5)java中类的格式: class 类名{ 属性-------变量 数据类型

C++返回引用类型 指针的引用(转载)

C++返回引用类型A& a(){ return *this;} 就生成了一个固定地址的指针,并把指针带给你但A a() { return *this;}会生成一个临时对象变量,并把这个临时变量给你这样就多了一步操作当返回一个变量时,会产生拷贝.当返回一个引用时,不会发生拷贝,你可以将引用看作是一个变量的别名,就是其他的名字,引用和被引用的变量其实是一个东西,只是有了两个名字而已.问题的关键是,当你想要返回一个引用而不是一个拷贝时,你要确保这个引用的有效性,比如:int & fun() {

浅谈Java中的equals和==(转载)

在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初学Java的时候这个问题不弄清楚,就

JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结--转载http://www.cnblogs.com/kubixuesheng/p/5202561.html

转载自---http://www.cnblogs.com/kubixuesheng/p/5202561.html 俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面的堆,栈 JVM的堆,栈和os如何对应 为啥方法的调用需要栈 属于月经问题了,正好碰上有人问我这类比较基础的知识,无奈我自觉回答不是有效果,现在深入浅出的总结下: 前一篇文章总结了:JV

转载:浅析Java中的final关键字

文章转自:http://www.cnblogs.com/dolphin0520/p/3736238.html 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下面是本文的目录大纲: 一.final关键字的基本用法 二.深入理解final关键字 若有不正之处,请多多谅解并欢迎指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cn

转载文章----C#基础概念

转载地址:http://www.cnblogs.com/zhouzhou-aspnet/articles/2591596.html 1.值类型和引用类型 1.1堆和栈 简单的说值类型存放在堆栈上面,引用类型的数据存放在托管堆上面(它的引用地址却存放在堆栈上面)! 栈:它是一个内存数组,是一个先进后出的数据结构! 栈的特征:数据只能从栈顶进,从栈顶出! 堆:它是一个内存区域,可以分配大块区域存储某类型的数据,与栈不同的是它里面的数据可以任意排序和移除! 下面是园子的一张图,贴上来供大家参考啊! 问

JavaScript基本类型和引用类型的值

JS变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型指的是简单的数据段,而引用类型值那些由多个值构成的对象: 在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值.JS有5种基本数据类型:Undefined, Null,Boolean,Number和String.这5种基本数据类型是按值访问的.因为可以操作保存在变量中的实际的值. 引用类型的值保存在内存中的对象.与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间.

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

自http://www.cnblogs.com/dolphin0520/p/3736238.html转载 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能被继承.也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰.final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方

【转载】C++ function、bind和lambda表达式

本篇随笔为转载,原贴地址:C++ function.bind和lambda表达式. 本文是C++0x系列的第四篇,主要是内容是C++0x中新增的lambda表达式, function对象和bind机制.之所以把这三块放在一起讲,是因为这三块之间有着非常密切的关系,通过对比学习,加深对这部分内容的理解.在开始之间,首先要讲一个概念,closure(闭包),这个概念是理解lambda的基础.下面我们来看看wikipedia上对于计算机领域的closure的定义: A closure (also le