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

争论

在 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放入该存储。 所以,一个指针和一个引用都占用了相同的内存量。

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

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

总结

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值