(转载请注明原创于潘多拉盒子)

一本典型的C语言教科书的厚度大约是200页左右,而一本典型的C++教科书的厚度至少要500页。比如K&R的《The C Programming Language》的厚度是272页,而权威性于此大致相当的Stroustrup的C++教科书《The C++ Programming Language》的厚度是1019页,后者是前者的3.75倍。这给C++工作者带来了沉重的负担,纯记忆这些内容就已非易事,能深入理解则消耗更多的实践,如果想把C++的每一个特性熟练恰当的运用,则近乎天方夜谭。

这源于C++的特性之复杂。为了在底层上兼容C并保持较高的运行效率,C++尽管号称对语言本身的特性进行了大大的限制,然而实际的结果并不理想。这里有大量的特性是程序员无法准确把握的,或者会造成误解,容易引起错误的。比如C++的默认参数特性,这个特性设计的本意是为了让C++的接口调用者使用起来更简单,或者对接口保持向下兼容,然而实际的效果并不理想。需要考虑的情况实在是太多了。对于一个含有默认参数的调用者来说,他需要搞清楚默认的参数是哪个,默认的情况下参数取值是什么,这些取值很多情况下并不是显而易见的,每到这样的一个地方就需要仔细的检查,看看是不是与接口设计的一致。非常容易发生错误。默认参数的个数还可能超过一个,这种情况下还夹杂类型隐式转换,实在是让人头疼不已。

因此不难看出,C++并非每一个特性都适合在实际中使用,而其中大量的特性,其实是实际中不适合使用的。因此很有必要有一个详细的介绍,能将C++中的优秀特性圈出来,让程序员在实际开发时优先选择这些优秀特性,而对其它C++特性的使用则采取谨慎的态度。

如果你之前用过别的高级语言,如Java/Python之类的,那么C++的引用特性可能有一部分是容易理解的,而有一部分则是有所不同的。

int a = 0;
int& b = a; // b是a的引用,对b的所有操作等同于作用到a上,包括作为左值和右值
b = 10; // 此时a = 10,b = 10
int* c = &b; // c持有的是a的地址,也是b的地址

这里的int& b定义了一个引用,需要立刻将被引用的对象作为右值赋值。

这段程序至少可以得到以下几个结论:

  1. 引用实际上是一个别名,相当于对变量换了个名字,其余不变(包括变量的地址)。
  2. 引用一旦指向一个对象,则这种指向关系不能变更。
  3. 对引用的操作等同于对它引用的对象进行操作。
  4. 引用的生命期包含于被引用对象的生命期(引用生命期开始晚于被引用对象生命期开始,结束则更早)。
  5. 引用不同于指针之处在于,引用关系是不能变更的;而指针的指向关系是可以变更的。实际上,一个引用大致相当于定义了一个const指针(int* const b = &a;)。
  6. 引用不能指向一个空对象(null)。

这里的引用和Python相同的点是#1,#3,#4;不同之处是#2,#5,#6。

引用出了可以定义变量之外,还可以作为形参。当引用作为形参时,传入的对象不会被拷贝,二是直接拷贝了地址。如:

int createFile(const std::string& filePath)
{
// 以filePath作为文件名创建文件
  return 0;
}

这里的filePath是一个std::string类型的对象,这个对象作为行参传入时,不会被复制,从而提高了效率。实际上,往往比这一点更重要的是,有些对象是无法复制的,比如锁、单例对象等。

当使用引用时,一个const修饰符往往是必要的。只要对象不需要在后续的代码中修改(mutate),那么就可以给该对象的引用加上const修饰符。关于const修饰符使用的场景,后面会介绍。

了解了一个特性之后,就有一个很重要的问题:什么场景下使用这个特性?总结下来看,对引用的使用,可以归结为以下几点:

  1. 对复杂表达式创建别名,提高可读性,降低思维成本。

std::vector<std::string> fields;
// 创建fields,填充值,这往往来自于一个parse操作,或者是一个split操作
const std::string& username = fields[0]; // 这里定义了一个别名,相比记忆fields[0]这种带脚标的表达式,实在是容易多了。
const std::string& age = fields[1]; // 还可以定义更多的别名
// 使用username和age,显著降低了思维成本,提高了代码的可读性

  2. 在形参中引用复杂的对象,避免对象拷贝。

  3. 返回一个对象的引用,避免拷贝。根据结论#4,返回的对象的生命期需要包含调用者取得引用的生命期。因此返回一个局部变量是不允许的。但可以返回一个成员变量、static对象、全局对象。

  4. 作为形参传入,用于保存函数对该形参的修改。这通常适合需要多返回值的情况,或者返回值是复杂对象,切不满足上述第3条,不是成员变量、static对象、全局对象。

然而,好的特性并不是可以被滥用的,如果定义一个下述的形参:

int nextNumber(const int& n)
{
return n + 1;
}

 则是完全不必要的,因为int型是一个基本类型,不是复杂类型,这样做就是“画蛇添足”。

为了让读者能专注在C++的特性上,这里的例子都是尽可能简单的。

C++的优秀特性1:引用的更多相关文章

  1. 总结Codeigniter的一些优秀特性

    总结Codeigniter的一些优秀特性 近期准备接手改进一个别人用Codeigniter写的项目.尽管之前也实用过CI,可是是全然按着自己的意思写的,没按CI的一些套路.用在公众的项目,不妨按框架规 ...

  2. C++的优秀特性6:智能指针

    (转载请注明原创于潘多拉盒子) 智能指针(Smart Pointer)是C++非常重要的特性.考虑如下一段使用简单指针(Plain Pointer)的代码: A* a = new A(); B* b ...

  3. JAVA8新特性——方法引用

    JAVA9都要出来了,JAVA8新特性都没搞清楚,是不是有点掉队哦~ 在Lamda新特性的支持下,JAVA8中可以使用lamda表达式来创建匿名方法.然而,有时候我们仅仅是需要调用一个已存在的方法(如 ...

  4. C++的优秀特性4:指针

    (转载请注明原创于潘多拉盒子) 其实指针不是C++的特性,而是地地道道的C的特性.有人说C++继承了C的指针,实在是败笔,造成内存泄漏云云,纯粹是不懂.可以这么说,如果没有指针,C++会逊色很多,应用 ...

  5. C++的优秀特性3:构造函数和析构函数

    (转载请注明原创于潘多拉盒子) 构造函数和析构函数是C++中再熟悉不过的概念了,几乎每个了解一点C++的人都知道这两个概念是什么意思.一个对象的全部生命期中构造函数和析构函数执行的时机如下: 1. 为 ...

  6. C++的优秀特性2:inline 函数

    (转载请注明原创于潘多拉盒子) Inline函数是C++的一个很小的特性,在不计较效率的情况下,这个特性似乎可有可无.然而,C++天生是为最为广泛的应用场景设计的,因此,总会有关于效率的问题.其实,除 ...

  7. Java 8 特性 —— 方法引用

    方法引用通过方法的名字来指向一个方法.方法引用可以使语言的构造更紧凑简洁,减少冗余代码.方法引用使用一对冒号 :: .下面,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不 ...

  8. java8新特性——方法引用与构造器引用

    上篇文章简单学习了java8内置得4大核心函数式接口,这类接口可以解决我们遇到得大多数得业务场景得问题.今天来简单学习一下方法引用与构造器引用. 一.方法引用 方法引用:若lambda 体中得内容已经 ...

  9. java8新特性-方法引用

    方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用 (可以将方法引用理解为 Lambda 表达式的另外一种表现形式) 1. 对象的引用 :: 实例方法名2. 类名 :: 静 ...

随机推荐

  1. 支持 MBTiles 规范的预缓存

    SuperMap iServer 支持生成符合MBTiles规范的预缓存(MBTiles是由MapBox制定的一种将瓦片地图数据存储到SQLite数据库中并可快速使用,管理和分享的规范. 该规范由Ma ...

  2. bjfu1277 简单递归

    比较简单的递归问题.对于第k时刻的图形,可以平均分成四块,左上,右上,左下这三块的图形是一模一样的,右下的那一块不包含红毛僵尸,所以把那三块里的加起来就是结果了. /* * Author : ben ...

  3. vector容器总结.xml

    1 清空所有元素     m_itemVector.clear();   2 遍历     vector<ITEM_CHECK>::iterator iter=m_itemVector.b ...

  4. bzoj 3809 Gty的二逼妹子序列(莫队算法,块状链表)

    [题意] 回答若干个询问,(l,r,a,b):区间[l,r]内权值在[a,b]的数有多少[种]. [思路] 考虑使用块状链表实现莫队算法中的插入与删除. 因为权值处于1..n之间,所以我们可以建一个基 ...

  5. spark connect to Cassandra problem

    Cassandra rowkey is Blob type, cannot select by spark. How?

  6. (转)android中利用 ViewPage 实现滑动屏

    最近实现了这样的一个效果:滑动界面出现拖拽效果,可翻动3屏,也可点击按钮翻动页面. 主要利用android.support.v4.view.ViewPager控件来实现. 第一个界面: 滑动屏幕: 换 ...

  7. configsections規範配置信息

    對於小型項目,配置信息可以通过appSettings进行配置,而如果配置信息太多,appSettings显得有些乱,而且在开发人员调用时,也不够友好,节点名称很容易写错,这时,我们有几种解决方案 1 ...

  8. [转]eoe社区cocos2d-x游戏引擎知识大汇总

    [eoeAndroid 社区]特意为大家汇总了cocos2d-x知识贴,分量十足,纯正干或.从基础教程到游戏应用的开发,我们不让知识流失,我们要做知识的搬运工还有加工 师.希望大家能够一起的学习,和大 ...

  9. 40个最好的Tumblr主题

    如果安装了一款较好的Tumblr主题,你的Tumblr空间将焕然一新.然而找到一款合适的主题并不是一件容易的事,这正是本文中我整理那么多优质的Tumblr模板作为灵感的原因.其中有一些免费的Tumbl ...

  10. mongodb使用中遇到的问题汇总

    1. 每次重新打开mongo,都会显示:forked process:xxxx ,然后用 find -name mongod.lock 进行搜索,发现在 ./var/lib/mongodb/ 目录下又 ...