c++当中为什需要引用

为什么需要引入引用?

  • 当初加入“引用”这个语言特性的契机是运算符重载。为了让运算符重载的语法能够更加接近内建的运算符,需要能够让一个函数返回一个左值,通俗的讲就是要能够对一个函数的返回值赋值。
  • 到了今天,引用除了原始用途之外,实际中最主要的用途是以==常量引用==来修饰函数的输入参数
  • ==输入参数,在函数内部不会被改变,此时用常量引用就是最合适的==

其他说法

  • 代替指针:引用比指针更加直观,a+b显然比(*a)+(*b)更易懂
  • 防止指针引起一些错误:==引用是不可能为空的==,一定确定一个引用,它的目标是不可以被改变的。如果一个类里面有个引用成员,它必须在构造的时候初始化,用它的时候,不必,也不能对它进行判空
  • 可以减少不必要的构造析构的开销

参数传递

void Func(Foo foo);
//这个定义的参数,会进行一次拷贝构造,创建一个临时对象传入Func函数
//效率低下浪费资源
void Func(Foo* foo);
//这是C的做法,把指针穿进去,免去调用拷贝构造函数创建临时对象的消耗
//但是引入了新的问题,foo可能为NULL,因此函数里不得不加上判断以免出现空指针调用问题
void Func(const Foo& foo);
//这是C++推荐做法,和指针传递性能一样,但免除了空指针的风险
//作为参数传递时引用和const总是一起出现的
  • 如果直接返回类对象同样的会创建临时对象带来开销,如果返回指针则调用者需要判断是否为空,而返回引用则可以非常高效的直接使用
  • 引用更加符合面向对象和隐藏实现细节的原则,而指针是更底层的机制。
  • ==实际上引用一般不会在同一作用域内被创建==,而是作为参数或者返回值

在引用出现以前,C程序员如果不想作值传递,只能用指针。指针的缺点主要有:

  1. 指代不直接。要通过->或者*来访问,要多打字而且不直观;
  2. 可以为空。所以当指代对象不能为空时,对指针要做null检查;
  3. 可以不赋初值。所以容易出错忘记赋值(现在编译器在警告方面好了很多)
  4. 指针可以被更改指代对象,更灵活也更容易出错

    • 引用和指针在某些时候也有不同的程序行为,比如dynamic_cast如果失败,指针转换返回null,而引用转换会出bad_cast异常。

注意

  • 因为引用不是对象,因此不能定义引用的指针。
  • 引用本身不是一个对象,因此不能定义引用的引用。

引用的底层实现方式

  • 引用变量在功能上等于一个指针常量,即一旦指向某一个单元就不能在指向别处
  • 在底层,引用变量由指针按照指针常量的方式实现。
  • 引用变量本身(以r为例)==不允许寻址==,&r返回的是被引用对象的地址,而不是变量r的地址(==r的地址由编译器掌握,程序员无法直接对它进行存取==),被引用对象直接用r表示。
  • 凡是使用了引用变量的代码,都可以转换成使用指针常量的对应形式的代码,只不过书写形式上要繁琐一些。反过来,由于对引用变量使用方式上的限制,使用指针常量能够实现的功能,却不一定能够用引用来实现。
  • ==数组元素允许是指针常量,却不允许是引用==。C++语言机制如此规定,原因是避免C++语法变得过于晦涩。加入定义一个“引用的数组”,那么array[0]=8;这条语句该如何理解?是将数组元素array[0]本身的值变成8呢,还是将array[0]所引用的对象的值变成8呢?对于程序员来说,这种解释上的二义性对正确编程是一种严重的威胁,毕竟程序员在编写程序的时候,不可能每次使用数组时都要回过头去检查数组的原始定义。
  • C++语言规定,引用变量在定义的时候就必须初始化,也即是将引用变量与被引用对象进行绑定。而这种引用关系一旦确定就不允许改变,直到引用变量结束其生命期。这种规定是在高级语言的层面上,由C++语言和编译器所做的检查来保障实施的。

理解:引用本身就是一个编译器管理的变量。底层使用的是指针进行实现。类似于常量指针。

猜你喜欢

转载自blog.csdn.net/zhc_24/article/details/81183071
今日推荐