C++的优秀特性1:引用
(转载请注明原创于潘多拉盒子)
一本典型的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定义了一个引用,需要立刻将被引用的对象作为右值赋值。
这段程序至少可以得到以下几个结论:
- 引用实际上是一个别名,相当于对变量换了个名字,其余不变(包括变量的地址)。
- 引用一旦指向一个对象,则这种指向关系不能变更。
- 对引用的操作等同于对它引用的对象进行操作。
- 引用的生命期包含于被引用对象的生命期(引用生命期开始晚于被引用对象生命期开始,结束则更早)。
- 引用不同于指针之处在于,引用关系是不能变更的;而指针的指向关系是可以变更的。实际上,一个引用大致相当于定义了一个const指针(int* const b = &a;)。
- 引用不能指向一个空对象(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:引用的更多相关文章
- 总结Codeigniter的一些优秀特性
总结Codeigniter的一些优秀特性 近期准备接手改进一个别人用Codeigniter写的项目.尽管之前也实用过CI,可是是全然按着自己的意思写的,没按CI的一些套路.用在公众的项目,不妨按框架规 ...
- C++的优秀特性6:智能指针
(转载请注明原创于潘多拉盒子) 智能指针(Smart Pointer)是C++非常重要的特性.考虑如下一段使用简单指针(Plain Pointer)的代码: A* a = new A(); B* b ...
- JAVA8新特性——方法引用
JAVA9都要出来了,JAVA8新特性都没搞清楚,是不是有点掉队哦~ 在Lamda新特性的支持下,JAVA8中可以使用lamda表达式来创建匿名方法.然而,有时候我们仅仅是需要调用一个已存在的方法(如 ...
- C++的优秀特性4:指针
(转载请注明原创于潘多拉盒子) 其实指针不是C++的特性,而是地地道道的C的特性.有人说C++继承了C的指针,实在是败笔,造成内存泄漏云云,纯粹是不懂.可以这么说,如果没有指针,C++会逊色很多,应用 ...
- C++的优秀特性3:构造函数和析构函数
(转载请注明原创于潘多拉盒子) 构造函数和析构函数是C++中再熟悉不过的概念了,几乎每个了解一点C++的人都知道这两个概念是什么意思.一个对象的全部生命期中构造函数和析构函数执行的时机如下: 1. 为 ...
- C++的优秀特性2:inline 函数
(转载请注明原创于潘多拉盒子) Inline函数是C++的一个很小的特性,在不计较效率的情况下,这个特性似乎可有可无.然而,C++天生是为最为广泛的应用场景设计的,因此,总会有关于效率的问题.其实,除 ...
- Java 8 特性 —— 方法引用
方法引用通过方法的名字来指向一个方法.方法引用可以使语言的构造更紧凑简洁,减少冗余代码.方法引用使用一对冒号 :: .下面,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不 ...
- java8新特性——方法引用与构造器引用
上篇文章简单学习了java8内置得4大核心函数式接口,这类接口可以解决我们遇到得大多数得业务场景得问题.今天来简单学习一下方法引用与构造器引用. 一.方法引用 方法引用:若lambda 体中得内容已经 ...
- java8新特性-方法引用
方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用 (可以将方法引用理解为 Lambda 表达式的另外一种表现形式) 1. 对象的引用 :: 实例方法名2. 类名 :: 静 ...
随机推荐
- ios ViewController 页面跳转
从一个Controller跳转到另一个Controller时,一般有以下2种: 1.利用UINavigationController,调用pushViewController,进行跳转:这种采用压栈和 ...
- jQuery Mobile中文手册:开发入门
jQuery Mobile 以“Write Less, Do More”作为目标,为所有的主流移动操作系统平台提供了高度统一的 UI 框架:jQuery 的移动框架可以让你为所有流行的移动平台设计一个 ...
- oracle 导入数据时提示只有 DBA 才能导入由其他 DBA 导出的文件
提示: IMP-00013: 只有 DBA 才能导入由其他 DBA 导出的文件 IMP-00000: 未成功终止导入 解决方法: 用户system用户登录然后授权 grant dba to hszx
- EasyDarwin返回401 Unauthorized解决方法
在向EsayDarwin发起 RTSP DESCRIBE请求或者ANNOUNCE推送时,EasyDarwin会返回401 Unauthorized: 原因:在EasyDarwin的RTSP Sessi ...
- Android intent-filter 简单用法
对电话拨号盘的过滤,mainfest配置文件中Activity如下配置: <activity Android:name=".TestActivity" android:lab ...
- 1.解剖Linq to object
LINQ想必大家都不陌生了,它的出现使得我们的代码变得更短.更优雅了.至于LINQ是什么,Linq to object这类的扩展方法到底做了些什么.我们使用的EF是如何实现的(如何解析Expressi ...
- [转] C#操作Excel文件
来自 jbp74c37ad170 的文章EXCEL编程语句有那些啊 全面控制 Excel首先创建 Excel 对象,使用ComObj:Dim ExcelID as Excel.Application ...
- JVM内存结构之三--持久代
本文会介绍一些JVM内存结构的基本概念,然后很快会讲到持久代,来看下Java SE 8发布后它究竟到哪去了. 基础知识 JVM只不过是运行在你系统上的另一个进程而已,这一切的魔法始于一个java命令. ...
- UIButton 在UIScrollView里面 点击效果不明显的问题
self.scrollView.delaysContentTouches = NO; -(BOOL)touchesShouldCancelInContentView { return YES; }
- eclipse 恢复被删除的文件
即使你在项目中删除了某一文件,该文件的相关信息仍会保存在本地历史记录中.这就使得你可以恢复那些在项目或文件夹中已被删除的文件.如果恢复某一被删除的文件,则首先在Navigator视图中选择该文件先前所 ...