zoukankan      html  css  js  c++  java
  • 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 << "	b= " << b << endl;//30 30
    	a = 40;
    	cout << "a= " << a << "	b= " << 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++中的引用








  • 相关阅读:
    poj 3616 Milking Time
    poj 3176 Cow Bowling
    poj 2229 Sumsets
    poj 2385 Apple Catching
    poj 3280 Cheapest Palindrome
    hdu 1530 Maximum Clique
    hdu 1102 Constructing Roads
    codeforces 592B The Monster and the Squirrel
    CDOJ 1221 Ancient Go
    hdu 1151 Air Raid(二分图最小路径覆盖)
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5393072.html
Copyright © 2011-2022 走看看