解析C++引用

 更新时间:2021年06月09日 10:20:39   作者:lsgxeva  
引用是C++引入的新语言特性,是C++常用的一个重要内容之一。在工作中发现,许多人使用它仅仅是想当然,在某些微妙的场合,很容易出错,究其原由,大多因为没有搞清本源。在本篇中将对引用进行详细讨论,希望对大家更好地理解和使用引用起到抛砖引玉的作用

引言

我选择写C++中的引用是因为我感觉大多数人误解了引用。而我之所以有这个感受是因为我主持过很多C++的面试,并且我很少从面试者中得到关于C++引用的正确答案。

那么c++中引用到底意味这什么呢?通常一个引用让人想到是一个引用的变量的别名,而我讨厌将c++中引用定义为变量的别名。这篇文章中,我将尽量解释清楚,c++中根本就没有什么叫做别名的东东。

背景

在c/c++中,访问一个变量只能通过两种方式被访问,传递,或者查询。这两种方式是:

1.通过值访问/传递变量

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

除此之外没有第三种访问和传递变量值的方法。引用变量也就是个指针变量,它也拥有内存空间。最关键的是引用是一种会被编译器自动解引用的指针。很难相信么?

下面是一段使用引用的简单c++代码

#include <iostream.h>  
int main()  
{  
    int i = 10;   // A simple integer variable  
    int &j = i;   // A Reference to the variable i  
    j++;   // Incrementing j will increment both i and j.  
    // check by printing values of i and j  
    cout<<  i  <<  j  <<endl; // should print 11 11  
    // Now try to print the address of both variables i and j  
    cout<<  &i  <<  &j  <<endl;  
    // surprisingly both print the same address and make us feel that they are  
    // alias to the same memory location.  
    // In example below we will see what is the reality  
    return 0;  
}   

引用其实就是c++中的常量指针。表达式int &i = j;将会被编译器转化成int *const i = &j;而引用之所以要初始化是因为const类型变量必须初始化,这个指针也必须有所指。下面我们再次聚焦到上面这段代码,并使用编译器的那套语法将引用替换掉。

#include <iostream.h>  
int main()  
{  
    int i = 10;            // A simple integer variable  
    int *const j = &i;     // A Reference to the variable i  
    (*j)++;                // Incrementing j. Since reference variables are   
                          // automatically dereferenced by compiler  
    // check by printing values of i and j  
    cout<<  i  <<  *j  <<endl; // should print 11 11  
    // A * is appended before j because it used to be reference variable  
    // and it should get automatically dereferenced.  
    return 0;  
}  

读者一定很奇怪为什么我上面这段代码会跳过打印地址这步。这里需要一些解释。因为引用变量时会被编译器自动解引用的,那么一个诸如cout << &j << endl;的语句,编译器就会将其转化成语句cout << &*j << endl;现在&*会相互抵消,这句话变的毫无意义,而cout打印的j值就是i的地址,因为其定义语句为int *const j = &i;

所以语句cout << &i << &j << endl;变成了cout << &i << &*j << endl;这两种情况都是打印输出i的地址。这就是当我们打印普通变量和引用变量的时候会输出相同地址的原因。

下面给出一段复杂一些的代码,来看看引用在级联(cascading)中是如何运作的。

#include <iostream.h>  
int main()  
{  
    int i = 10; // A Simple Integer variable  
    int &j = i; // A Reference to the variable  
    // Now we can also create a reference to reference variable.   
    int &k = j; // A reference to a reference variable  
    // Similarly we can also create another reference to the reference variable k  
    int &l = k; // A reference to a reference to a reference variable.  
    // Now if we increment any one of them the effect will be visible on all the  
    // variables.  
    // First print original values  
    // The print should be 10,10,10,10  
    cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <<endl;  
    // increment variable j  
    j++;   
    // The print should be 11,11,11,11  
    cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <<endl;  
    // increment variable k  
    k++;  
    // The print should be 12,12,12,12  
    cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <<endl;  
    // increment variable l  
    l++;  
    // The print should be 13,13,13,13  
    cout<<  i  <<  ","  <<  j  <<  ","  <<  k  <<  ","  <<  l  <<endl;  
    return 0;  
}  

下面这段代码是将上面代码中的引用替换之后代码,也就是说明我们不依赖编译器的自动替换功能,手动进行替换也能达到相同的目标。

#include <iostream.h>  
int main()  
{  
    int i = 10;         // A Simple Integer variable  
    int *const j = &i;     // A Reference to the variable  
    // The variable j will hold the address of i  
    // Now we can also create a reference to reference variable.   
    int *const k = &*j;     // A reference to a reference variable  
    // The variable k will also hold the address of i because j   
    // is a reference variable and   
    // it gets auto dereferenced. After & and * cancels each other   
    // k will hold the value of  
    // j which it nothing but address of i  
    // Similarly we can also create another reference to the reference variable k  
    int *const l = &*k;     // A reference to a reference to a reference variable.  
    // The variable l will also hold address of i because k holds address of i after  
    // & and * cancels each other.  
    // so we have seen that all the reference variable will actually holds the same  
    // variable address.  
    // Now if we increment any one of them the effect will be visible on all the  
    // variables.  
    // First print original values. The reference variables will have * prefixed because   
    // these variables gets automatically dereferenced.  
    // The print should be 10,10,10,10  
    cout<<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <<endl;  
    // increment variable j  
    (*j)++;   
    // The print should be 11,11,11,11  
    cout<<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <<endl;  
    // increment variable k  
    (*k)++;  
    // The print should be 12,12,12,12  
    cout<<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <<endl;  
    // increment variable l  
    (*l)++;  
    // The print should be 13,13,13,13  
    cout  <<  i  <<  ","  <<  *j  <<  ","  <<  *k  <<  ","  <<  *l  <<endl;  
    return 0;  
}  

我们通过下面代码可以证明c++的引用不是神马别名,它也会占用内存空间的。

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

结论

我希望这篇文章能把c++引用的所有东东都解释清楚,然而我要指出的是c++标准并没有解释编译器如何实现引用的行为。所以实现取决于编译器,而大多数情况下就是将引用实现为一个const指针。

引用支持c++虚函数机制的代码

#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  
         return 0;  
}  

上述代码使用引用支持虚函数机制。如果引用仅仅是一个别名,那如何实现虚函数机制,而虚函数机制所需要的动态信息只能通过指针才能实现,所以更加说明引用其实就是一个const指针。

以上就是解析C++引用的详细内容,更多关于C++引用的资料请关注脚本之家其它相关文章!

相关文章

  • C语言与C++中const的用法对比

    C语言与C++中const的用法对比

    C语言中的const与C++有很大的不同,在C语言中用const修饰的变量仍是一个变量,表示这个变量是只读的,不可显示地更改,而在C++中用const修饰过后,就变成常量了
    2022-04-04
  • C语言实现猜数字游戏

    C语言实现猜数字游戏

    这篇文章主要为大家详细介绍了C语言实现猜数字游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • 基于C++实现一个日期计算器

    基于C++实现一个日期计算器

    这篇文章主要为大家详细介绍了如何利用C++实现一个简单的日期计算器,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2022-10-10
  • C++ 的cout格式化输出场景示例详解

    C++ 的cout格式化输出场景示例详解

    这篇文章主要为大家介绍了C++的cout格式化输出场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • openCV实现图像分割

    openCV实现图像分割

    这篇文章主要为大家详细介绍了openCV实现图像分割,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • opencv实现棋盘格检测

    opencv实现棋盘格检测

    这篇文章主要为大家详细介绍了opencv实现棋盘格检测,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • c语言全盘搜索指定文件的实例代码

    c语言全盘搜索指定文件的实例代码

    c语言全盘搜索指定文件的实例代码,需要的朋友可以参考一下
    2013-03-03
  • C++ Boost Any示例分析使用

    C++ Boost Any示例分析使用

    Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称
    2022-11-11
  • Matlab实现绘制立体玫瑰花的示例代码

    Matlab实现绘制立体玫瑰花的示例代码

    这篇文章主要介绍了如何利用Matlab实现绘制更立体的玫瑰花,文中的示例代码讲解详细,对我们学习Matlab有一定的帮助,需要的可以参考一下
    2023-02-02
  • C++类和对象基础详解

    C++类和对象基础详解

    类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫类的实例化。每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数
    2021-08-08

最新评论