条款1:尽量以const和inline取代#define
另一个关于define的缺陷是,define定义的符号可能没有机会编译进来或者在其它头文件中有一个相同的符号,结果被覆盖了。至于inline,通常与templete联用。
条款2:尽量以<iostream>取代<stdio.h>
老式输出流方式应该扔掉了,别忘了二级考试scanf吃的大亏。一个例外是strstream,它有很多陷阱,已经建议用stringstream代替。不过在处理大文件的时候,stream的方式还是比较慢的。
条款3:尽量以new和delete取代malloc和free
原因在与malloc和free对构造函数和析构函数一无所知。千万不要混用!
条款4:尽量使用C++风格注释形式
它更安全,如果你的IDE没有高级的语法着色。
条款5:使用相同形式的new和delete
这个规则主要是delete和delete[],当然作者结出了更好的建议,使用标准库中的string或vector代替数组的需求。
条款6:记得在destructor中以delete对付pointer members
很多人都知道,但常常忘记。顺便指出,delete一个NULL指针不会有什么问题,但要小心野指针。作者给出的建议是,使用智能指针。
条款7:为内存不足的状况预做准备
现实中是,很多人都避而不谈。处理它你可能要捕获异常,书中用set_new_handler来指定处理,这样更灵活。
条款8:撰写operator new和operator delete时应遵行的公约
这一章太深奥了,我从未写过new和delete操作符。
条款9:避免遮掩了new的正规形式
条款10:如果你写了一个operator new,请对应写一个operator delete
条款11:如果class内动态配置内存,请为此class声明一个copy constructor和一个assignment运算符
这个却实重要,但我常常忽略这一点。
条款12:在constructor中尽量以initialization动作取代assignment动作
通俗地说,构造函数的 ':' 列表。除了性能上的优势,其它如const成员变量只能初始化而不能赋值。但是如果要初始化的很多,还是用assigment比较好。
条款13:initialization list中的members初始化次序应该和其在class内的声明次序相同
你应该明白的一个道理:成员变量初始化顺序与initialization list中的顺序无关,跟声明次序有关。比如,很明显data members的destructors总是和其constructors次序相反,但构造函数的inilialization list的顺序可能的情况太多,这会给编译器增加负担。
条款14:总是让base class拥有virtual destructor
每个类在生命期结束时都会调用destructor,如是鸺不把destructor放到vtbl中,vptr就会指向一个非法地址,产生的结果将无法预料!!!请记住‘总是’。
条款15:令operator=传回"*this的reference "
为什么传回引用很简单。也许你会想能不能传回参数中那个rhs?如果参数被定义为const编译不会通过,即使你让它编译通过了,它还是有潜在的bug,不要尝试。
条款16:在operator=中为所有的data members设定(赋值)内容
这样做是为了,如果你的类被用来继承,你的子类很有可能不知道如何对父类的成员赋值,通常是直接调用父类的oprator=,Base::operator=(rhs)或static_cast<Base&>(*this)=rhs。
条款17:在operator=中检查是否“自己赋值自己”
通常在给一个变量赋值前,会释放以前的值或内存,现在你知道其原因了吧。
条款18:努力让接口完满且最小化
条款19:区分member functions,non-member fuctions(比如全局函数)和friend functions三者
假设f是你企图声明的一个函数,C是个class,
◆ 虚拟函数必须是class members。如果f必须是虚拟函数,那么就让它成为C的一个member funcion。
◆ 绝不要让operator>>和operator<<成为members。如果f是operator>>或operator<<,请让f成为一个non-member function;如果f同时用到 C 的non-public members,请让f成为C的一个friend。
◆ 只有non-member functions才能在其最左端引数身上实施型别转换。如果f需要在其最左端引数身上施行型别转换,请让f成为一个non-member fuction;如果f同时还必须取胜C的non-public members,请让地成为C的一个friend。
◆ 除此以外,都应该设计成member function.
条款20:避免将data members放在公开接口中
抛开面向对象教条的束缚,在安全上也得不偿失(你也许不得不把它声明为volatile)。
条款21:尽可能使用const
将const写在member function后面,指它不能当作左值。经典的string类刘示范
class String {
public:
... ...
char& operator[](int position) // 针对non-const objects而设计的operator[]
{ return data[position];}
const char& operator[](int position) const // 针对const objects而设计的operator[]
{ return data[position];}
... ...
};
String s = "hello"; //这是一个non-const object
cout<<s[0]; //OK,读一个non-const object
s[0] = 'x'; //OK,写一个non-const object
const String cs = "hello"; //这是一个const object
cout<<cs[0]; //OK,读一个const object
s[0] = 'x'; //错误,写一个const object,这是声明在后面那个const的功劳
条款22:尽量使用pass-by-reference(传址)少用pass-by-value(传值)
这条规则不仅应该应用在参数中,返回值中也应该使用。
条款23:当你必须传回object时,不要尝试传回reference
虽然条款22推荐什么reference,但如果返回一个局部对象的refrence,必然结果会出错。
条款24:在函数重载(function overloading)和参数缺省化(parameter defaulting)之间,谨慎抉择
条款25:避免对指针型别和数值型别进行重载
比如
void f(int x);
void f(string *ps);
f(0) //到底调用的是那个?
条款26:防卫潜伏的ambiguity(模棱两可)状态
事实上,这种情况经常出现。特别是隐式转换时,可能有多种方式,如果运气好,编译器会给出警告,否则,它将是一个潜伏很深的bug。
条款27:如果不想使用编译器暗自产生的member functions,就应该明白拒绝它
遇到最多的是assignment和copy construction
条款28:尝试切割global namespace(全局命名空间)
如果你在一个项目中做一部份,最好在你的头文件中包括namesapce,
条款29:避免传回内部数据的handles
其实是不要传回局部对象
条款30:避免写出member functions,传回一个non-const pointer或reference并以之指向较低存取层级的members
条款31:千万不要传回“函数内local对象的reference”或“函数内以new获得的指针所指的对象”
这是一个常识。
条款32:尽可能延缓变量定义式的出现(亦即需要时才定义之)
在C语言中,你是没办法这样做,在C++中,这样做不仅没必要,而且效率不高。抛弃C的传统吧。
条款33:明智地运用inlining
首先,inline非常不利于调试,而且会增加出现奇怪bug的机会(不管你信不信,这是事实)。正确的做法是,在debug版本时不使用inline,在release时,通过测试工具找出调用最频繁的函数,再决定使用inline,当然,你应该注意代码膨胀。
条款34:将文件之间的编译依赖关系(compilation dependencies)降至最低
最直观的表现是,在头文件中尽量不使用#include引用其它头文件,使用声明,除非万不得已。在写头文件时,先不要写#include,等编译不过时,再来看看这条。
条款35:确定你的public inheritance,模塑出“isa”的关系
可以认为这是面向对象精神的要求,实现中违反这条的不在少数。
条款36:区分接口继承(interface inheritance)和实现继承(impliementation inheritance)
这个问题的关键在于“protected"关键字的使用,因为它决定了两者。(不要与COM混为一谈)
条款37:绝对不要重新定义继承非而来的非虚函数
最好听从这个建义,虽然你的类目前工作正常,但它的确是一个隐患。
条款38:绝对不要重新定义继承而来的缺省参数值
当然,如果你信奉第37条,这里应该指虚函数的参数。因为默认参数是静态绑定的,编译器可能会给你一个错误的参数。
条款39:避免在继承体系中做向下转型(cast down)动作
很显然,向下转换或称之为dynamic_cast是不安全的,而且很多时候完全可以通过virtual来避免。
条款40:通过layering技术来模塑has-a或is-implemented-in-terms-of的关系
在设计模式中,称之为代理(proxy)
条款41:区分inheritance和templates
相同点是,它们都是代码复用机制之一,区别在于,template应该产生一群class,并且类型的改变不会影响函数的行为。
条款42:明知地运用private inheritance(私有继承)
很少看到这种使用方法,它只有两个能有所用处的特质:1、编译器不会自动转换private继承的子类到你类;2、父类的所有成员函数都会变成private。
条款43:明智地运用多继承(multiple inheritance,MI)
首先声明,它确实很有复杂性。
条款44:说出你的意思并了解你所说的每一句话
简而言之,在写继承时,你应该知道为什么要这样写。
条款45:清楚知道C++(编译器)默默为我们完成和调用哪些函数
对于一个类,编译器乐于为你写下面的函数
class A {
public:
A();
A(const A& rhs);
~A();
A& operator=(const A& rhs);
A* operator&();
const A* operator&() const
}
但你不要轻信了编译器,它做的可能并不好
条款46:宁愿编译和连接时出错,也不要执行时才出错
这点告诫你,对编译器的警告也不能忽视;也不能为了让编译器通过而故意修改你不明白的地方。
条款47:使用non-local static objects之前先确定它已有初值
在我写COM时,对CoCreateInstnce传入了一个未初始化的指针,理论上它是一个[out]型参数,所以我认为没必要初始化,其结果是花了一个晚上找这个bug。
条款48:不要对编译器的警告信息视如不见
很简单
条款49:尽量让自己熟悉C++标准程序库
知己知彼,百战百胜。
条款50:加强自己对C++的了解
两本高阶C++设计方面的书:The Design and Evolution of C++ ,The Annotoated C++ Refrence Manual. 很遗憾,我还没有拜读过。
后记:终于把这本书看完了。这本书的意义我就不多说了,这里只想阐述一下个人对写这篇博客(笔记)的感觉。其实在一年多以前,我曾读过这本书,本书以条款为主线来安排布局,读起来非常轻松。比如我规定每天看5个条款,算下来十天就能看完,工作量很小,你甚至能在睡觉前当作枕边书来看(它的催眠效果还不错)。不过只看而不记下来,时间长了很快就会忘记。本书罗列50个条款,通常情况下,不能解决你手头上的麻烦;有些条款可能你永远都不会用到。如果写下来,一个好处就是,在你需要的时候一眼应能看出来,这正是笔记的好处。另外,与此书齐名的还有More Effecitve C++,同样是Scott Meyers写的,希望能抽出时间完成下一篇读书笔记。(2009年09月)