【分析】深入探究 C++ 引用

来源:互联网 发布:模拟基金交易软件 编辑:程序博客网 时间:2024/06/01 08:44

争论

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

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

关于引用,我翻看了一些网上的博客和外文文章,看到两种观点:

  • 引用为一个参数的别名(当然,C++ primer 上也是这么说的)

  • 引用其实就是一个常量指针,指向这个对象(所以必须被初始化才能使用,和常量指针很类似),由编译器进行解释自行翻译成常量指针

个人看法

其实以上的两种说法都有一定的正确性,但是我们其实可以两个都当作正确的(只要能够正确使用就行),当然我还是认为后一个观点正确一点(仅仅是个人观点),因为我认为C++ 中并没有所谓的别名的这个特性,如开头所说:

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

很多的引用别名的特性其实都可以通过常量指针的特性得到解释,引用的基本特性如下:

  • 引用不可以为空,必须在初始化的时候绑定上一个对象,即必须初始化
  • 引用不可以改变指向(奇怪的特性)
  • 对引用的操作是直接反应到所绑定变量之上(同样经过 * 取地址的常量指针也具有这个特性)
  • 引用的大小是所指向的变量的大小(和指针类似,不过这里要给pointer 加上 *在进行sizeof())

比如最基本的,定义一个引用对象,必须进行初始化,测试代码如下:

#include<iostream>using namespace std;int main() {    int a = 5;//  int &b;   //编译器报错,提示需要初始化引用//  int *const b; //编译器同样报错,提示需要初始化     int &b = a;    int *const &c = &a;    cout << a <<" "<< b <<"  "<< *c << endl;    // print  5 5 5    a++;    cout << a << " " << b << "  " << *c << endl;    //print 6 6 6    return 0;}

可以看到,这个引用的特性可以用常量指针的形式得到解释,定义一个int &b = a; 类似于定义一个int *const &c = &a;

使用的时候,类似的,编译器会给这个引用解释成 int * const 进行解释

针对不可以改变引用的指向,同样可以用常量指针进行解释。由于对引用的使用都是给这个引用解释成 int * const 然后给上*进行操作,所以sizeof()的结果也是原对象的大小。

最重要的一点让我认为引用不是对象的别名的就是,引用也会占用内存空间(当然啦,不占内存空间的对象应该不存在),如下:

#include<iostream>using namespace std;class A {    int &i;   // int *const i;      int &j;   // int *const j;      int &k;   // int *const k;   };int main() {    cout << "sizeof(A): " << sizeof(A) << endl;    //print   sizeof(A): 12     system("pause");    return 0;}

引用的地址空间和原对象的地址一样:

#include<iostream>using namespace std;int main() {    int a = 5;    int &b = a;    cout << &a << endl;    //00CFFA00    cout << &b << endl;    //00CFFA00    system("pause");    return 0;}
最后还有最重要的一点,引用对虚函数的支持,这个我想已经没有什么好说的了。如果引用只是一个对象的别名,那么就应该绑定到彻底。而只有指针可以提供虚函数的动态支持。

如下代码:

#include<iostream>using namespace std;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(); // print C      A a2 = c1;    a2.print(); // print A      system("pause");    return 0;}

最后再贴一点stack overflow上关于引用和指针的描述:

  • 指针可以重新分配任意次数,而参考在绑定后无法重新配置。
  • 指针可以指向无处(NULL),而引用总是引用一个对象。
  • 你可以用指针来取代引用的地址。
  • 没有“参考算术”(但是您可以使用引用指向的对象的地址,并在其中进行指针算术&obj + 5),如下所示。

澄清一个误解: C ++标准非常小心,以避免指示编译器如何实现引用,但每个C ++编译器都将引用作为指针。也就是说,一个声明如: int &ri = i; 如果没有完全优化,则分配与指针相同的存储空间,并将地址i放入该存储。 所以,一个指针和一个引用都占用了相同的内存量。

  • 作为基本规则, 在函数参数和返回类型中使用引用来定义有用的和自洽的接口。

  • 使用指针来实现算法和数据结构。

总结

 看到这里我们应该已经可以知道了,我对这两种说法都没有意见,只是对翻译有一点点意见。 引用只是一种被语言特性劫持了的常量指针,具体实现就是用编译器自动替换转换成常量指针的地址取值,一点自己的理解,可能有很多的偏颇。 但是我提供了一点新的思路