绝对不要对无效的迭代器执行解引用(dereference)操作
用于不要将异常安全性放在事后考虑。异常安全性会影响到类的设计。它永远都不会“只是一个实现细节”。
在传递对象参数时,选择const&方式而不是传值方式。
对于程序运行中不会改变的值,应该预先计算并保存起来备用,而不是重复地创建对象,这是没有必要的。
通常,为了保持一致性,应该使用前置递增来实现后置递增,否则,当其他用户在使用你的类时,可能会得到奇怪结果。
优先选择使用前置递增。只有在需要初始值时,才使用后置递增。
在进行隐式类型转换时,要注意在转换过程中创建的 临时对象。要避免这个问题,一个好办法就是尽可能地通过显式的方式来构造对象,并避免编写类型转换运算符。
记住对象的生存期。永远,永远,永远都不要返回指向局部对象的指针或引用;它们没有任何用处,因为主调代码无法跟踪它们的有效性,但却可能会试图这么做。
尽可能地重用代码——尤其是标准库中的代码——而不是自己去编写代码,这样更快、更容易,也更安全。
如果在函数中不打算处理所抛出的异常,那么应该将异常转发给能够进行处理的上层调用者。
在编写代码时应该始终遵循:即使在出现异常时,资源仍然能够被正确地释放,并且数据也总是处于一致的状态。
遵循标准的异常安全规则:永远不要在析构函数、重载运算符函数operator delete()或者operator delete[]()中抛出异常; 在编写每个析构函数和内存释放函数时,要假设存在着“throw()”这样的异常规范。
遵循标准的异常安全性规则:在每个函数中,要将所有可能会抛出异常的代码单独放在一起,并且对这些代码进行安全处理。然后,当你确认这些代码执行的工作都已经成功地完成时,才可以使用不会抛出异常的操作来修改程序的状态。
永远都不要到最后才实现异常安全性。异常安全性会对类的设计产生影响。它永远都不会“只是一个实现细节”。
优先考虑实现内聚。要努力使每段代码——每个模块、每个类、每个函数——都只有单一的,并且是明确定义的功能。
“异常不安全”总是与“拙劣的设计”结伴的。如果程序的设计逻辑清晰,那么即使有一段代码不是异常安全的,一般来说也不会有太大问题,并且可以很简单地进行修正。但如果有一段代码由于设计问题而不能被编写成异常安全的,我们通常都会认为这个设计时拙劣的。下面是两个拙劣设计的示例。
示例1:如果在一个函数中需要实现两个不同的功能,那么这个函数很难被编写成异常安全的。
示例2:如果在拷贝赋值运算符函数中必须对自我赋值进行检测,那么这个函数也可能不是完全异常安全的
遵循标准的异常安全性规则:以“获得资源也就意味着初始化”这种模式来分离资源的所有权和资源的管理权。
在进行设计中,要始终牢记重用性。
优先采用“ a op=b;”这种写法,而不是"a = a op b;"(这里的op表示某个运算符)。这种写法更为清晰,效率也高。
如果定义了某个运算符(例如,operator+),那么通常还应该同时定义与这个运算符相对应的赋值运算符(例如,operator+=)。并且用后者来实现前者。而且,还应该维护op和op=之间的自然关系。
在C++标准中规定:运算符=,(),[]和->必须被定义为成员函数,而在类中定义的new,new [],delete和delete[]等运算符函数必须是静态成员函数。对于其他的运算符函数:
如果运算符函数是用于流I/O的opeator>>或者operator<<,或者如果运算符函数需要对其左操作数进行类型转换,或者运算符函数可以通过类的公有接口来实现,那么将这个函数定义为非成员函数(在前两种情况中,如果需要的话也可以被定义为友元函数);如果运算符函数需要实现虚函数的行为,那么增加一个虚函数来提供虚函数的行为,并用这个虚成员函数来实现运算符函数否则将预算富函数定义为成员函数。
在函数opeator>>和operator<<中应该始终返回对流对象的引用。
将基类的析构函数定义为虚函数(除非你能保证,永远都不会有人通过指向基类的指针来删除派生类的对象)。
如果在派生类中定义的函数与基类中的函数有相同的名字,并且你不想隐藏基类中函数,那么应通过using声明语句将基类的这个函数引入到派生类的作用域中。
永远不要改变被覆盖的基类函数中的默认参数值。
除了对真正的Liskov IS-A和WORKS-LIKE-A关系进行建模之外,永远都不要使用共有继承。所有被覆盖的成员函数不能超过实际需求的范围,同时也不能小于这个范围。
使用公有继承的目的是重用代码(编写以多态的方式使用基类对象的代码),而重用(基类中的)代码并不一定要使用公有继承。
对“is implemented in terms of”这种关系建模时,应该优先选择成员关系/包含的方式,而不是私有继承的方式。只有非用继承不可时,才应该使用私有继承——也就是说,当需要访问保护成员或者需要覆盖虚函数时,才使用私有继承。永远都不要只是为了代码重用而使用共有继承。
对于广泛使用的类,应该优先使用编译器防火墙这种惯用法(也叫做Pimpl惯用法)来隐藏实现细节,通过一个不透明的指针(指向一个进行了前置声明但又没有定义的类)来保存私有成员(包括状态变量和成员函数),声明这个指针时可采用“struct XxxxImpl* pImpl;XxxxImpl* pimpl_;”这样的形式。例如:“class map{ private :struct MapImpl;MapImpl* pimpl_;}”
包含,也可以叫做“聚合”,“分层”,“HAS-A”或者“委托”。优先选择包含而不是继承,对于IS-IMPLEMENTED-IN-TERMS-OF这种关系建模时,应该优先考虑使用包含,而不是继承。