`
zhaohaolin
  • 浏览: 979045 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

C++中的引用(reference)

 
阅读更多

1.简介

    引用是C++引入的新语言特性。从语意上来说,引用就是一个变量的别名,就好象古代人的“字”和“号”,东坡居士和苏轼只是一个人的不同称呼。对引用的操作对变量产生的影响与对变量直接操作完全一样。例如:

int i = 0;
int & iRef = i;

iRef++;  // i = iRef = 1

    尽管引用不使用指针的操作符(*, ->)但是,它看上去跟指针好象并没有区别,而且就上面的例子而言,这个引用所产生的作用完全可以由指针完成。那么为什么C++中还要增加这样一个特性呢?引用显然应该具备指针不能完成的功能,否则它就失去了价值。这方面的探讨我们留到第3节。

2.引用的语法

在这里我们只讨论一些语法相关的问题。

· 引用必须在定义的同时初始化

int i;
int & j;  // 错误,没有初始化。
int & k=i; // 正确

    这个例子有个很好的比喻,小时候小朋友间会互相起“外号”,这些外号在产生的时候总是有所指的,即针对一个具体的小朋友的。引用也一样,定义的时候,必须指明它是谁的别名。

· 外部(extern)引用定义不必给出初值

extern int & i; // 正确,不必给出初值

· 引用初始化后不能再使其成为其它变量的引用

int j, k;
int & i = j;
i = k;    // 错误,不能更改!

    引用类似一个常量指针(int * const p),不能修改引用的指向。

· 引用的地址

    假设有如下定义:

int j;
int & i = j;

    那么,&i应该是什么呢?是一个“引用的地址”么?答案是:no。&i = &j,就是j这个变量的地址。

3.引用使用技巧

3.1 引用和多态

    引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。例如:

class A;
class B: public A
{
   ...
};

B b;
A & aRef = b;    // 基类引用指向派生类

    如果A类中定义有虚函数,并且在B类中重写了这个虚函数,就可以通过aRef产生多态效果。

3.2 作为参数

    引用的一个重要作用就是作为函数的参数类型。C/C++的函数参数是传值的,如果有大对象(例如一个大的结构)需要作为参数传递的时候,以前的(C语言 中)方案往往是指针,因为这样可以避免将整个对象全部压栈,可以提高程序的效率。但是现在(C++中)又增加了一种同样有效率的选择,就是引用。

    与指针类型的参数一样,引用不论指向什么类型的对象,作为参数传递的时候都是只压栈4个字节(在32位机上)。引用所占用的4字节大小是根据编译器产生的代码判断的,因为sizeof(a_reference)只能得到它所指向对象的大小。

    引用型参数应该在能被定义为const的情况下,尽量定义为const,这不光是让代码更健壮,也有些其它方面的需要,例如,假设有如下函数声明:

string foo();
void bar(string & s);

    那么下面的表达式将是非法的:

bar(foo());
bar("hello world");

    原因在于foo()和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。

3.3 作为返回值

    引用作为返回值的时候,有一些规则必须遵守。这些规则包括:

  • 不能返回局部变量的引用。 这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了“无所指”的引用,程序会进入未知状态。
  • 不能返回函数内部new分配的内存的引用。 这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
  • 可以返回类成员的引用,但最好是const。 这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

    另外,引用也常常与一些操作符的重载相关:

  • 流操作符<<和>>。这两个操作符常常希望被连续使用,例如:cout << "hello" << endl;因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一 个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个 流指针则不能连续使用<<操作符。因此,返回一个流对象引用是唯一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就 是C++语言中引入引用这个概念的原因吧。
  • 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的唯一返回值选择。

    在另外的一些操作符中,却千万不能返回引用:

  • +-*/四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一 个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

3.4 什么时候使用引用

    现在可以总结一下什么时候使用引用这个问题了。首先我们要看看什么时候必须使用引用:

  • 流操作符<<和>>、赋值操作符=的返回值
  • 拷贝构造函数的参数、赋值操作符=的参数

    其它下面的情况都是推荐使用引用,但是也可以不使用引用。如果不想使用引用,完全可以使用指针或者其它类似的东西替代:

  • 异常catch的参数表
  • 大对象作为参数传递
  • 返回容器类中的单个元素
  • 返回类数据成员(非内建数据类型成员)
  • 返回其它持久存在的,且获得者不负责销毁的对象

    另外一些情况下,不能返回引用:

  • +-*/四则运算符

 

4.参考资料

[1] Effective C++: Scott Meyers

分享到:
评论

相关推荐

    c++注释引用参考(中文版本)

    The Annotated C++ Reference Manual 如今已经不够完整(它出版之後,C++ 又新增了一些语言特性— 见条款 35),从某方面说已经过气,但它仍然是语言核心方面(包括 templates 和 exceptions)的最佳参考书籍

    C++中Reference与指针(Pointer)的使用对比

    了解引用reference与指针pointer到底有什么不同可以帮助你决定什么时候该用reference,什么时候该用pointer。  在C++ 中,reference在很多方面与指针(pointer)具有同样的能力。虽然多数C++程序员对于何时使用...

    C++11右值引用和转发型引用教程详解

    为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念。右值引用采用T&&这一语法形式,比传统的引用T&(如今被称作左值引用 lvalue reference)多一个&。 如果把经由T&&...

    C++关于引用作为函数的用法

    引用是C++中特有的语法,在C语言中不存在。 本质上引用(reference)就是指针,在类型名后面加上一个&号就是引用类型。 1.指针与引用的定义进行比较 指针定义: 引用定义: int a = 123; int a =123; int* p = &a;...

    C C++之指标 (pointer)参考 (reference) 观念整理与常见问题

    技术文章 对于 C /C++ 的 Pointer 有详细的说明, 看完之后收获很大

    C++传值调用与引用调用区别实例代码

    C++ comparing call_by_value(传值调用) and call_by_reference(引用调用)

    c++ const引用与非const引用介绍

    // error: nonconst reference to a const object 非const引用是指向非const类型变量的引用。 const引用可以初始化为不同类型的对象或者右值(如字面值常量),但非const引用不可以。 代码如下: //

    C++标准之(ravalue reference) 右值引用介绍

    1、右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题。但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了CopyElision、RVO(包括NRVO)等编译器优化技术...

    北邮C++课件 第06章 指针和引用

    北邮C++课件 第06章 指针和引用 指针(Pointer)是C++和C的一种数据类型。很多其他高级语言也有类似...引用(Reference)则是C++所特有的一种数据类型。指针和引用在概念上和使用上都有相似之处,但是也有重要的差别。

    C++智能指针实现

    它的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为...

    More Effective C++

    条款29:Reference counting(引用计数) 183 条款30:Proxy classes(替身类、代理类) 213 条款31:让函数根据一个以上的对象型别来决定如何虚化 228 Making functions virtual with respect to more than one ...

    简单介绍C++中变量的引用

    对一个数据可以使用“引用(reference)”,这是C++对C的一个重要扩充,引用是一种新的变量类型,它的作用是为一个变量起一个别名。假如有一个变量a,想给它起一个别名b,可以这样写: int a; //定义a是整型变量 ...

    Effective C++ 3rd(中文版)part1

    它也记述了像 smart pointers(智能指针),reference counting(引用计数)和 proxy objects(代理对象)这样的重要的 C++ 编程技术。 Effective STL 像 Effective C++ 一样是一本面向指导方针的书,但是它专注于...

    Effective C++ 3rd(中文版)part2

    它也记述了像 smart pointers(智能指针),reference counting(引用计数)和 proxy objects(代理对象)这样的重要的 C++ 编程技术。 Effective STL 像 Effective C++ 一样是一本面向指导方针的书,但是它专注于...

    C++11 Matrix

    1. 使用右值引用(r-value reference),大大减少矩阵运算中的临时矩阵数量,提供直观的调用语法和高空间效率; 2. 使用并发编程(std::async),充分利用多核CPU,对矩阵运算效率进行优化,对外接口不变。

    Effective+C+++3rd+chm+中文版(代码加亮)

    如何在 pass-by-value(传值)和 pass-by-reference(传引用)之间选择?在一开始就做出正确的决定是很重要的,因为一个不好的选择可能会直到开发过程很晚的阶段才显现出来,在这时候再调整它常常是困难重重,极为...

    reference.cpp

    引用必须被初始化,并且以后不能再引用别的对象.

    C++11 模板参数的“右值引用”是转发引用吗

    在C++11中,&&不再只有逻辑与的含义,还可能是右值引用: void f(int&& i); 但也不尽然,&&还可能是转发引用: template void g(T&& obj); “转发引用”(forwarding reference)旧称“通用引用”(universal ...

    C++中智能指针如何设计和使用

    它的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为...

Global site tag (gtag.js) - Google Analytics