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++中的引用








  • 相关阅读:
    Oracle 常用的十大 DDL 对象
    Oracle DML
    Oracle 的常用概念
    Spring 4 : 整合 SSH
    Spring3 (事务管理)
    Spring2
    Spring 学习笔记一
    Xpath helper下载
    爬取链家北京市二手房的单个房源页信息
    爬取链家北京市二手房的链家编号
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5393072.html
Copyright © 2011-2022 走看看