By Herb Sutter, Andrei Alexandrescu 著
树人译
函数和操作符
25. 合理地对待通过传值,传(智能)指针或传引用的参数。
合理地确定参数:区分输入,输出和输入/输出参数,区分值参数和引用参数。合理地对待它们。
26. 保留被重载操作符的自然语义。
程序设计人员并不喜欢惊奇:只有在有适当的理由时采取重载操作符,而且要保留自然的语义;如果那样做很困难的话,那你可能滥用操作符重载了。
27. 优先使用算数和赋值操作符的规范形式。
如果有a+b的话,也应该有a+=b:在定义二元算数操作符时,也要提供它们对应的赋值版本,而且要使重复最小,效率最佳。
28. 优先使用++和—操作符的规范形式。优先调用前缀形式。
如果有++c的话,也应该有c++:由于递增和递减操作符有各自的前缀和后缀形式,而且语义也稍有不同,这样就显得它们有些棘手。定义模仿其内建副本行为的operator++和operator—。如果你不需要原始值的话,优先调用前缀版本。
29. 考虑用重载来避免隐式类型转换。
不去增加需求之外的对象(奥卡的剃刀):隐式类型转换提供了语法上的便利(但参见Item40)。但是当创建临时对象的工作没有必要而且优化要适当的时候,你可以提供重载函数,它的型构与公共的参数类型精确地匹配,而且不会引起转换动作。
30. 避免去重载&&,||,或,(逗号)。
贤人知道适可而止:编译器会特殊对待内建的&&,||和,(逗号)。如果要重载它们,它们就成了普通函数,有非常不同的语义,而且这是引入微妙的bug和脆性的一个“可靠的”途径。
31. 不要编写依赖于函数实参评估顺序的代码。
保持(评估)顺序:一个函数的实参的评估顺序没有被指定,所以不要依赖于一个特定的顺序。
类设计和继承
32. 搞清楚你正在编写的类的种类。
了解你自己:有许多不同种类的类。了解你正在编写的类的种类。
33. 优先使用最小型的类,而不是整体式的类。
分而治之:小型类更容易编写,正确,测试和使用。在多数情况下小型类更可能合用。优先使用这些包含了简单概念的小型类,而不是那些试图实现很多或者是复杂的概念的大型类(参见:Item5和Item6)。
34. 优先使用组合,而不是继承。
避免承受继承的负担:在C++中继承是仅次于友元的第二紧密的耦合关系。紧耦合是不受欢迎的,而且要尽可能地避免它。因此,优先使用组合,而不是继承,除非你知道后者真正有益于你的设计。
35. 避免从那些没有被设计用来当作基类的类派生。
一些人并不想要孩子:那些被用来独立使用的类与基类相比,它们执行着不同的计划(参见Item32)。把独立的类当作基类是一种严重的设计错误,我们应该避免它。要增添行为,优先增加非成员函数,而不是成员函数(参见Item44)。要增加状态,优先使用组合,而不是继承(参见Item34)。避免从具体基类继承。
36. 优先使用抽象的接口。
热爱抽象艺术:抽象接口可以帮你集中于得到一个正确的抽象概念,而不用把它和实现或状态管理细节混在一起。优先设计这样的层次结构,它实现了对抽象概念建模的抽象接口。
37. 公有继承就是具有可替换性。继承不是重用,但可以被重用。
Know what:公有继承可以让一个指向基类的指针或引用去引用某个派生类的一个对象,而且既不会破坏代码的正确性也不需要变更既有代码。Know why:不要通过公有地继承来重用代码(也就是基类中存在的代码);共有地继承是为了被重用(通过那些已经多态地使用了基类对象的既有代码)。
38. 使用安全的改写(overriding)。
在改写一个虚拟函数时,要保持可替换性;特别地,要注意在基类中函数的前置和后置条件。不要变更虚拟函数的默认实参。优先考虑显式地把改写的函数重新声明为virtual。小心会隐藏基类中的重载函数。
39. 考虑让虚拟函数非公有,让公有函数非虚拟。
在基类中变更(特别是在程序库和框架中)的代价是非常高的:优先让公有函数非虚拟。优先让虚拟函数私有化或者(如果派生类需要有调用基类版本的能力的话)受保护。(注意这个建议对析构函数不适用;参见Item50)
40. 避免提供隐式的转换。
并非所有的变更都是改进:隐式的转换经常是害大于利。在提供隐式的转换前,重新考虑一下你定义的类型,并且优先依赖于显式的转换(explicit构造函数和具名的转换函数)。
41. 让数据成员私有化,除非是在一些更小的聚合体中。(C风格的结构体)。
这并非调用者的事:让数据成员私有化。只有在那些聚合了一堆值但不需要封装或提供操作的简单的C风格结构体类型的情况下,才可以把所有数据成员声明为公有的。避免把公有和非公有的数据混合在一起,这往往意味着一个混乱的设计。
42. 不要出卖你的内部数据。
不要自作多情:避免返回一个类所管理的内部数据的句柄,这样用户就不能不受控制地修改对象所拥有的状态。
43. 明智地使用Pimpl。.
克服语言的分离欲望:C++可以让私有成员不可访问,但并不是不可见。考虑使用Pimpl惯用法让私有成员真正不可见,从而实现编译器防火墙和提高信息隐藏。(参见Item11盒Item41)
44. 优先编写非成员非友元的函数。
避免友元的耗费:只要可能,就优先让函数既非成员又非友元。
45. 总是提供配对的new和delete。
这是一揽子交易:每个重载了void* operator new(parms)的类都必须同时提供一个对应的重载void operator delete(void*, parms),这里的parms是一个额外的形参类型列表(第一个总是std::size_t)。数组形式的new[]和delete[]也是一样。
46. 如果你提供了任何在类中声明的new,就要提供所有标准形式(plain,in-place和nothrow)。
不要隐藏标准形式的new:如果一个类定义了operator new的任何一种重载形式,就应该提供plain,in-place和non-throwing所有三种形式的operator new的重载。你如果不提供的话,它们会被隐藏掉,而且对你的用户也是不可用的。