攻城狮凌风

C++中的引用详解

1.引用的本质 

   

   在 C/C++中,变量仅能且只能通过两种方式被访问、传递或获取。即:

        通过值 访问 / 传递变量

        通过地址 访问 / 传递变量 – 这种方法就是指针

   引用常被认为是变量的别名,实际上 C++ 中根本就没有什么叫做别名的定义。引用变量也就是个指针变量,它也拥有内存空间。最关键的是引用是一种会被编译器自动解引用的指针。引用本质上被编译成指针常量(constant pointers)。这意味着:
int &i = j  --->   int *const i = &j 

引用的常见例子

#include <iostream.h>
int main()
{
    int i = 10;
    int &j = i;<pre name="code" class="cpp">    int* const k=&i;  
    j++; cout<<i<<j<<*k<<endl; //相同的值 11 11 11
    (*k)++;
    cout<<i<<j<<*k<<endl; //相同的值 12 12 12
    cout<<&i<<&j<<k<<endl;//相同的 地址 
     return 0;}


       为什么打印相同的地址?引用变量时会被编译器自动解引用,诸如"cout << &j << endl;"的语句,编译器就会将其转化成语句"cout << &*j << endl"。

    这样来理解。如前描叙,j实际为指针常量,即int *const j= &i。所以语句"cout << &i << &j<< endl"变为"cout << &i << &*j<< endl"也就是"cout << &i << j<< endl"。所以打印相同的地址。


2.引用的级联(cascading)

看下面的代码

#include <iostream.h>
int main()
{
    int i = 10; // A Simple Integer variable
    int &j = i; // A Reference to the variable
    int &k = j; // A reference to a reference variable
    int &l = k; // A reference to a reference to a reference variable.
 
    cout<<i<<","<<j<<","<<k<<","<<l<<endl;// The print should be 10,10,10,10
    j++;
    cout<<i<<","<<j<<","<<k<<","<<l<<endl;// The print should be 11,11,11,11
    k++;
    cout<<i<<","<<j<<","<<k<<","<<l<<endl;// The print should be 12,12,12,12
    l++;
    cout<<i<<","<<j<<","<<k<<","<<l<<endl;// The print should be 13,13,13,13
    return 0;}


   不依赖编译器的自动替换功能,手动进行替换也能达到相同的目标。上面的代码等同于

#include <iostream.h>
int main()
{
    int i = 10;        
    int *const j = &i;   
    int *const k = &*j;  
    int *const l = &*k; 
    
    cout<<i<<","<<*j<<","<<*k<<","<<*l<<endl;// The print should be 10,10,10,10

    (*j)++; 
    cout<<i<<","<<*j<<","<<*k<<","<<*l<<endl;// The print should be 11,11,11,11
    (*k)++; 
    cout<<i<<","<<*j<<","<<*k<<","<<*l<<endl;// The print should be 12,12,12,12
    (*l)++;
    cout<<i<<","<<*j<<","<<*k<<","<<*l<<endl;// The print should be 13,13,13,13 
    return 0;}


     其中j,k.l的value都是i的地址


3.引用占据内存

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

        C++标准并没有解释编译器如何实现引用的行为。所以实现取决于编译器,而大多数情况下就是将其实现为一个const指针。因此引用仍然占据内存。



4.引用支持虚函数机制

#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,此时实质为 A a2=(A)c1
         return 0;
}

   引用支持虚函数机制,而虚函数的动态信息只有通过指针实现。更加说明引用其实就是一个const指针。

   


5.数组引用和常量引用 

  ❶数组引用

#include"iostream"
using namespace std;
void display(const int a[],int length){
    int i=0;
	while(i<length)
	  {cout<<a[i]<<"   ";i++;}
}
void add(int a[],int length){
    int i=0;
	while(i<length)
	  {a[i]++;i++;}
}
int main()
{
	int a[5]={0};
	int (&b)[5]=a;
	display(a,5); cout<<endl; //输出 0 0 0 0 0
	display(b,5); cout<<endl; //输出 0 0 0 0 0
	add(b,5);
	display(a,5); cout<<endl;//输出 1 1 1 1 1
	display(b,5); cout<<endl;//输出 1 1 1 1 1
	return 0;
}

  ❷常量引用

#include"iostream"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
	int a = 30;
	const int &b = 30;
	cout <<"a= " << a << "\tb= " << b << endl;//30 30
	a = 40;
	cout << "a= " << a << "\tb= " << b << endl;//30 40
	return 0;
}

   ❸对于指针也有对应的引用。


6.引用作为形参和返回类型

           Person old;
           Person new=old;

          在JAVA中,使用引用传递,修改old则new值也变化。C++中除数组外,默认为值传递,即隐式的调用了Person类的默认拷贝构造函数。C++中同类型的对象赋值基本为值传递。

    引用做为形参,能够避免拷贝时间,提高效率。但是不能传递局部变量给上一级引用变量。如:

Person test(Person &p){
       .......
return p;}
Person &pNew=test(p);//报错

           因为return p到 =test(P)过程中间,程序调用了默认拷贝函数,生成了一个test函数局部域的临时Person对象,而临时变量在test函数结束的时候销毁了。因为引用类型变量时必须初始化为一个已经定义并且有效的对象,所以调用失败了。

     ❷引用也可以作为传递类型,这时候复制不会调用拷贝构造函数:

Person& test(Person &p){
       .......
     return p;}
Person &pNew=test(p);//不调用拷贝函数
Person pNew=test(p);//调用拷贝构造函数

  

   

7.总结


    C++标准规定,引用可以占内存也可以不占,取决于编译器实现。

    ❶占内存的实现就是使用指针了。很多编译器为了简单都全部采用这种实现方法。

    ❷不占内存的实现,是引用和引用的对象在同一个函数中的时候。局部变量名其实是一个相对于栈基址的偏移量,这种情况下你定义这个局部变量的引用,编译器可以直接给这个引用赋同样的偏移值,这样引用名和变量名完全没有区别。这就是别名的含义了。

    ❸引用为形参只有用指针实现这种方法。例如换值函数swap,以下两种定义等同:

                  void swap(int const* a,int const * b )  
                  void swap(int&a, int&b)

 



参考:

    1.深入分析C++引用

     2.C++中的引用








posted on 2014-12-26 13:58  攻城狮凌风  阅读(251)  评论(0编辑  收藏  举报

导航