C++覆辙录 作者:(美)STEPHEN C. DEWHURST
C++覆辙录 出版社:人民邮电出版社
C++覆辙录 内容简介
《C++覆辙录》是C++大师Stephen C. Dewhurst根据多年教授C++课程中所遇到的常见错误的心得笔记编写而成。本书所有章节都从一个众所周知的、在日常编码或设计实践经常遭遇的问题入手,先指出其不足,再对其背后思想中存在的合理与不合理之处深入剖析,取其精华,去其糟粕,给出一个简洁、通用的方案,给出如何规避或纠正它们的建议,从而有助于C++软件工程师避免重蹈前辈的覆辙。
《C++覆辙录》适合具有一定C++编程经验的读者阅读。
C++覆辙录 目录
前言
致谢
第1章 基础问题
常见错误1:过分积极的注释
常见错误2:幻数
常见错误3:全局变量
常见错误4:未能区分函数重载和形参默认值
常见错误5:对引用的认识误区
常见错误6:对常量(性)的认识误区
常见错误7:无视基础语言的精妙之处
常见错误8:未能区分可访问性和可见性
常见错误9:使用糟糕的语言
常见错误10:无视(久经考验的)习惯用法
常见错误11:聪明反被聪明误
常见错误12:嘴上无毛,办事不牢
第2章 语法问题
常见错误13:数组定义和值初始化的语法形式混淆
常见错误14:捉摸不定的评估求值次序
常见错误15:(运算符)优先级问题
常见错误16:for语句引发的理解障碍
常见错误17:取大优先解析原则带来的问题
常见错误18:声明饰词次序的小聪明
常见错误19:“函数还是对象”的多义性
常见错误20:效果漂移的型别量化饰词
常见错误21:自反初始化
常见错误22:静态连接型别和外部连接型别
常见错误23:运算符函数名字查找的反常行为
常见错误24:晦涩难懂的operator ->
第3章 预处理器问题
常见错误25:使用#define定义的字面量
常见错误26:使用#define定义的伪函数(函数宏)
常见错误27:#if的滥用
常见错误28:断言(assert宏)的副作用
第4章 型别转换问题
常见错误29:以void *为型别转换的中介型别
常见错误30:截切问题
常见错误31:对目标型别为指涉物为常量的指针型别的型别转换的认识误区
常见错误32:对以指涉物为指涉到常量的指针型别的型别为目标型别的型别转换的认识误区
常见错误33:对以指涉物为指涉到基类型别的指针型别的型别为目标型别的型别转换的认识误区
常见错误34:指涉到多维数组的指针带来的问题
常见错误35:未经校验的向下转型
常见错误36:型别转换运算符的误用
常见错误37:始料未及的构造函数型别转换
常见错误38:在多继承条件下进行强制型别转换
常见错误39:对非完整型别做强制型别转换
常见错误40:旧式强制型别转换
常见错误41:静态强制型别转换
常见错误42:形参引发临时对象生成的初始化
常见错误43:临时对象的生存时域
常见错误44:引用和临时对象
常见错误45:动态强制型别转换运算符dynamic_cast带来的多义性解析失败
常见错误46:对逆变性的误解
第5章 初始化问题
常见错误47:赋值与初始化混淆
常见错误48:位于非适当辖域的变量
常见错误49:未能意识到C++语言中复制操作的固守行为
常见错误50:按位复制的class对象
常见错误51:未能区分构造函数中的初始化和赋值
常见错误52:未能在成员初始化列表中保持次序一致性
常见错误53:对于虚基类(子对象)进行默认初始化
常见错误54:复制构造函数对基类子对象初始化的未预期行为
常见错误55:运行期静态初始化次序
常见错误56:直接vs.复制初始化
常见错误57:对实参的直接初始化
常见错误58:无视返回值优化
常见错误59:在构造函数中初始化静态数据成员
第6章 内存和资源管理问题
常见错误60:未能区分纯量与数组的内存分配机制
常见错误61:内存分配失败校验
常见错误62:用自定义版本替换全局的内存管理运算符所调用的函数
常见错误63:成员版本的operator new和operator delete的辖域和调用机制混淆
常见错误64:抛出字符串字面常量作为异常对象
常见错误65:未能正确理解和利用异常处理机制
常见错误66:滥用局部量地址
常见错误67:未能采用RAII习惯用法
常见错误68:对auto_ptr的误用
第7章 多态问题
常见错误69:型别特征码
常见错误70:将基类析构函数声明为非虚函数
常见错误71:对非虚成员函数的遮掩
常见错误72:以过分灵活的方式滥用模板方法设计模式
常见错误73:重载虚函数
常见错误74:为实参指定默认初始化物的虚函数
常见错误75:在构造函数和析构函数中调用虚函数
常见错误76:虚赋值
常见错误77:未能区分函数的重载、改写和遮掩
常见错误78:未能深入理解虚函数和改写的实现机制
常见错误79:支配原则议题
第8章 型别设计问题
常见错误80:取/设状态接口
常见错误81:常量和引用数据成员
常见错误82:未能理解常量成员函数
常见错误83:未能区分强聚合和弱聚合
常见错误84:非适当的运算符重载
常见错误85:运算符优先级和重载
常见错误86:友元vs.成员运算符
常见错误87:自增/自减运算符的问题
常见错误88:对模板化的复制操作的认识误区
第9章 继承谱系设计问题
常见错误89:持有class对象的数组
常见错误90:非适当的容器型别之可替换性
常见错误91:未能理解protected访问层级
常见错误92:为代码复用而以public方式继承
常见错误93:以public方式继承具象类
常见错误94:未能运用继承谱系的退化形式
常见错误95:继承的滥用
常见错误96:依型别分派的控制结构
常见错误97:单根谱系
常见错误98:向class对象打探隐私
常见错误99:权能查询问题
中英文术语对照表
引用书目
C++覆辙录 精彩文摘
说一个问题是基础的,并不就是说它不是严重的或不是普遍存在的。事实上,本章所讨论的基础问题的共同特点比起在以后章节讨论的技术复杂度而言,可能更侧重于使人警醒。这里讨论的问题,由于它们的基础性,在某种程度上可以说它们普遍存在于几乎所有的C++代码中。
常见错误1:过分积极的注释
很多注释都是画蛇添足,它们只会让源代码更难读,更难维护,并经常把维护工程师引入歧途。考虑下面的简单语句:
a = b; // 将b赋值给a
这个注释难道比代码本身更能说明这个语句的意义吗?因而它是完全无用的。事实上,它比完全无用还要坏。它是害人精。首先,这条注释转移了代码阅读者的注意力,增加了阅读量因而使代码更费解。其次,要维护的东西更多了,因为注释也是要随着它描述的代码的更改而更改的。最后,这个对注释的更改常常会被遗忘:
c = b; // 将b赋值给a
仔细的维护工程师不会武断地说注释是错的,所以他就被迫要去检视整个程序以确定到底是注释错了呢,还是有意为之呢(c可能是a的引用),还是本来正确只是比较间接的呢(赋值给c可能引发一些传播效应以使a的值也发生相应变化),等等,总之这一行就根本不应该带注释。
a = b;
还是这代码本来的样子最清楚地表明了其意义,也没有额外的注释需要维护。这在精神上也符合老生常谈,亦即“最有效率的代码就是根本不存在的代码”。这条经验对于注释也适用:最好的注释就是根本用不着写的注释,因为要注释的代码已经“自注释”了。
另一些常见的非必要的注释的例子经常可以在型别的定义里见到,它们要么是病态的编码标准的怪胎,要么就是出自C++新手:
class C {
// 公开接口
public:
C(); // 默认构造函数
~C(); // 析构函数
// ...
};
你会觉得别人在挑战你的智商。要是某个维护工程师连“public:”是什么意思都需要教,你还敢让他碰你的代码吗?对于任何有经验的C++软件工程师而言,这些注释除了给代码添乱、增加需要维护的文本数量以外没有任何用处:
class C {
// 公开接口
protected:
C( int ); // 默认构造函数
public:
virtual~C(); // 析构函数
// ...
};
软件工程师还有一种强烈的心理趋势就是尽量不要“平白无故”地在源文件文本中多写哪怕一行。这里公布一个有趣的本行业秘密:如果某种结构(函数啦、型别的公开接口啦什么的)能被塞在一“页”里,也就在三四十行左右的话,它就很容易理解。假如有些内容跑到第二页去了,它理解起来就难了一倍。如果三页才塞得下,据估计理解难度就成原来的4倍了。一种特别声名狼藉的编码实践就是把更改日志作为注释插入到源文件的头部或尾部:
/* 6/17/02 SCD把一个该死的bug干掉了 */
这到底是有用的信息,抑或是仅仅是维护工程师的自吹自擂?在这行注释被写下以后的一两个星期,它怎么看也不再像是有用的了,但它却也许要在代码里粘上很多年,欺骗着一批又一批的维护工程师。最好是用你的版本控制软件来做这种无用注释真正想做的事,C++的源代码文件里可没有闲地方来放这些劳什子。
→→→→→→→→→→→→→→→→→→→→查找获取
评论