永远不要用#include包含不必要的头文件
如果只需要流的前置声明,应该优先使用#include<iosfwd>
只需要前置声明时,绝不要用#include包含相应的头文件。
如果使用聚合关系就已经足够,就不要使用继承。
要避免使用内联或者复杂的调整方法,除非通过性能分析证明这确实是必要的。
正确使用名字空间。如果将一个类放入名字空间,那么同时要保证将这个类的所有辅助函数和运算符函数也放入相同的名字空间。否则,你将在代码中发现奇怪的结果。
要理解这五种不同类型的内存,了解他们为什么是不同的,以及他们各自的行为又是怎么样:栈(自动变量)、自由存储(new/delete)、堆(malloc/free)、全局(静态变量、全局变量、文件作用域变量等)、常量数据(字符串常量等)。
优先使用自由存储(new/delete),避免使用堆(malloc/free)。
对于“堆”和“自由存储”进行区分,这一点很重要,因为在C++标准中有意避开了这两种类型的内存是不是相关的这个问题。例如,当通过::operator delete()函数来释放内存时,在C++标准的18.4.1.1中,最后一项是这样的:
“ 在C++标准中并没有规定,在哪些情况下,在通过operator delete回收的存储空间中,有一部分或者全部的控件可以再随后调用operator new或者calloc,malloc以及realloc等函数时被重新分配,这些函数的声明时在<cstdlib>中。”
而且,在C++标准中也没有规定,new/delete是否需要通过malloc/free来实现。不过,在C++标准20.4.6节的第3段和第4段中规定了,malloc/free一定不能使用new/delete来实现:“calloc、malloc和realloc函数不会通过调用::operator new()来分配存储空间。函数free()不会通过调用::operator delete() 来释放内存。”
如果在类中定义了new和delete中的任意一个运算符函数,那么一定要同时定义另外一个。
通常应该显式地将函数operator new ()和operator delete()声明为静态函数。他们永远都不能使非静态成员函数。
永远都不要通过多态的方式处理数组。
优先选择使用vector或者deque,而不是数组。
在编写拷贝赋值运算符函数时,永远都不要指望能够通过对自我赋值进行检测来保证函数的正确性;应该在拷贝赋值运算符函数中使用“创建临时对象并进行交换”的惯用法,这种方法不仅是异常安全的,而且在处理自我赋值时也是安全的。
可以将自我赋值检测作为一种优化手段,以避免不必要的工作,这是正确地做法。
不仅要避免编写类型转换运算符函数,而且还要避免编写隐式的构造函数。
尽量编写异常安全的代码。在编写代码时应该始终遵循:即使在出现异常时,资源仍然能够被正确地释放,并且数据也总是处于一致的状态。
避免使用语言中那些不常用的特性,而应该使用最简单并且有效的技术。
拷贝初始化过程绝不是赋值过程,因此在初始化中永远都不会调用函数T::operator=()。是的,我知道在初始化语句中有一个“=”符合,但不要被它迷惑。它只是从C语言中沿用过来的一种语法,并不代表赋值运算。
如果可能的话,优先使用“T t(u);”这种形式,而不是“T t=u;”的形式。通常,能能够时候后者的地方,都可以使用前者,并且使用前者还有更多的好处——例如,可以带多个参数。
在函数声明中,如果参数是以传值方式来传递的,则不要使用const。而如果在这个函数的定义中,参数是不能被修改的,那么应该使用const。
对于不是内置类型的返回值来说,当使用返回值的方式而不是返回引用的方式时,应该优先选择返回const值。
const 和mutable都是你的朋友
优先使用新形式的类型转换。
不要通过类型转换去掉常量属性,而应该使用mutable。
避免使用向下的类型转换。
优先通过引用方式来传递对象参数,而不是传值方式,并且在所有可能的地方都使用const。
避免使用内联,除非从性能的分析上来看确实有必要这么做。
避免使用全局变量或者静态变量。如果必须使用,那么一定要特别注意这些变量的初始化顺序。
在构造函数的初始化列表中,应该把 基类按照他们在类定义中出现的先后顺序进行排列。
在编写代码时,永远都不应该依赖函数参数的求值顺序