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

一本典型的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. HEAD

    Branches are just pointers to commits Every branch is simply a named pointer to a commit. A special ...

  2. Yii连接多个数据库的方法

    一.配置多数据库 大多数情况下,我们都会采用同一类型的数据库,只是为了缓解压力分成主从或分布式形式而已.声明你可以在 主配置文件 ( main.php )   中里声明其它的数据库连接: <?p ...

  3. redhat 下Redis安装

    Redis 官网:http://redis.io/  下载地址: http://redis.io/download 安装方法:   cd opt wget http://download.redis. ...

  4. Raspberry Pi3 ~ 配置网络

    Rpi3 有两个网卡 一个无线wlan 一个有线 eth0 无线的只需要在右上角的那个配置里面添加就行 有线的需要设置下静态IP.dns.等 在raspbain图形化界面里面 设置 Network P ...

  5. CAT XQX --- 省市三级级联实现说明

    最终效果: 满足要求, 上代码 : 1.   需要调用这个控件 的地方:添加引用,因为里面写着逻辑呢..... <script type="text/javascript" ...

  6. 链接服务器"(null)"的 OLE DB 访问接口 "Microsoft.Jet.OLEDB.4.0" 返回了消息 "未指定的错误"。[手稿]

    消息 7302,级别 16,状态 1,第 1 行 无法创建链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.JET.OLEDB.4.0&qu ...

  7. Spring框架入门:(非原著,转载)

    1.1.      耦合性和控制反转: 对象之间的耦合性就是对象之间的依赖性.对象之间的耦合越高,维护成本越高.因此,对象的设计应使类和构件之间的耦合最小. 例: public interface I ...

  8. C语言的 (强制类型转换) 以及 '字符字面值'

    C语言的显式/隐式类型转换,都有一个中间变量的存在,原数据的类型.内容都不变. 以下代码,都用GCC编译. #include<stdio.h> int main() { char c = ...

  9. 数往知来C#之接口 值类型与引用类型 静态非静态 异常处理 GC垃圾回收 值类型引用类型内存分配<四>

    C# 基础接口篇 一.多态复习 使用个new来实现,使用virtual与override    -->new隐藏父类方法 根据当前类型,电泳对应的方法(成员)    -->override ...

  10. Embedded之memory test

    1 main.c #include "led.h" #define BASE_ADDRESS (volatile datum *) 0x10000000 #define NUM_B ...