《code rule 101》

来源:互联网 发布:百货批发软件哪个好 编辑:程序博客网 时间:2024/05/16 10:17

书英文名叫《C++ Coding Standards: 101 Rules, Guidelines, and Best Practices》每天看一则准则,并且将准则的大致内容和理解翻译在此系列中。以期望可以加深印象和理解的目的。

第12条:
弄清何时以何种方式实现并发编程

Summary:
如果你的程序使用多线程或多进程,懂得如何尽可能少的使用共享对象,并且能够安全的使用共享对象。

并发编程最重要的主题是避免死锁,活锁和恶性竞态。

如果你的程序在线程间共享数据,你应该做以下事情:
1)查看目标平台文档里相关的同步原语。
2)最好把平台提供的同步原语用自己的抽象方式包装以下,这样在写跨平台程序的时候可以做到通用性。
3)确保在多线程环境下你的使用方式是安全的,最好你的使用方式至少应满足以下两点:
a)确保非共享对象是独立的
b)将对同一个对象在不同线程中应该如何调用文档化;

第13条:
确保对象拥有资源,并且使用RAII和智能指针

第14条:
让错误尽可能暴露在编译或链接阶段而不是运行阶段

第15条:
使用const保护机制

第16条:
避免使用宏

第17条:
避免使用幻数

第18条:
尽可能减少变量的作用域
如若不然:
1)使得程序难懂难维护
2)会污染命名空间
3)变量时常不被正确初始化。

第19条:
总是初始化变量

第20条
避免过长或嵌套过深的函数
否则:不易维护,代码不好读

第21条
避免编译过程中的初始化依赖
否则:由于各个单元的初始化顺序在编译期间不能保证,所以如果存在初始化依赖,容易导致初始化失败。

第22条
尽量减少定义的依赖,避免循环依赖
好处:减少定义的依赖可以使得各个类型定义可以尽可能的独立发布。特别是在不同的模块间,减少定义依赖尤为重要。

模块:整体测试和发布的由同样的人或团队开发且具有内聚性的类或函数的集合。

第23条
使头文件能够独立编译
否则:如果包含一个头文件导致必须包含另一个头文件则给用户带来额外的负担。
目标:在你的编译系统中,单独编译任何一个头文件,并且保证没有错误或告警发生。

第24条
应当使用头文件卫士,并且应始终书写内部卫士而非外部卫士。
内部卫士:写在本头文件中,保证此文件只会被包含一次
外部卫士:在其他文件包含此头文件时做判断是否已经包含
如:对于一个头文件foo.h 内部包含就是在foo.h中书写如下语句:

#ifndef _FOO_H_#define _FOO_H...#endif

外部包含是指在其他需要包含foo.h的文件中,比如source.cpp中:

#ifndef _FOO_H_#include "foo.h"#define _FOO_H_#endif

如果采用外部包含,则需要在每次包含foo.h时都做判断,很麻烦。

第25条
正确选择使用值,指针或引用传参
原则:传值一般用于开销不大的场合,对于传指针或引用的特殊之处,有以下区分:
传指针:在调用时可以传null表示不关心。
传引用:调用时必须传一个有效的参数。

第26条
保持重载操作符的自然语义

第27条
使用正统的算数操作符和赋值操作符
正统:主要是指在算数操作符重载时要保持算术操作符的传统语义,比如a+b为右值,a+=b为左值,并且在实现时要尽量保持最大效率和最少的代码重复度。

第28条
忠于传统的++前和后++实现方式,首先想到调用前缀形式
传统实现方式如下:

T& t::operator++()  // prefix form{    // do increment    return *this;}T T::operator++(int)  // postfix form{    T old (*this);    ++*this;   // call the prefix one    return old;}

第29条
考虑重载函数以避免隐式的类型转换
原因:隐式类型转换可能产生新的临时对象,以降低代码效率

第30条
不要重载&&,||,,(comma逗号)运算符
原因:因为内置的这三种操作符都有从左到右执行,且&&和||具有短路特性,而如果重载的话,做不到从左到右执行和短路的特点。会引发应用时的歧义。

第31条
不要依赖于函数的参数赋值顺序编码
举例:callFunc (++count, ++count); // order of evaluation

第32条
弄清楚需要编写哪种类
值类, 基类,traits类,策略类,异常类

第33条
使用小类而不是巨类

第34条
喜欢组合而非继承
原因:继承是仅次于友元的强耦合关系
组合:在一个类中嵌入另一个类型的变量
组合相对继承好处:
1)接口使用有较大灵活性
2)编译的独立性和简洁性
3)代码怪异性减少:如继承引发的名字覆盖等
4)更广泛的适用性
5)代码更强壮安全
6)较少的复杂性

第35条
避免从那种不是设计成基类的类继承
如果要扩展功能,尽量增加非成员函数,这样用到以前的类的地方不会受到影响,否则,如果采用继承的方式增加成员函数,那么以前的那个类的使用会受到很大影响。
如:
1)父子类之间类型转换问题
2)父子类之间名字覆盖问题
3)析构函数设计不当导致的内存释放问题

第36条
应当提供抽象接口
抽象接口:全部由虚函数并且没有数据成员组成的类。
遵从依赖倒置原则。
依赖倒置原则(DIP):高层模块不应依赖于底层模块,而应相反;抽象不应依赖于实现细节,而应相反。
依赖倒置好处:
1)改进鲁棒性:因为接口教实现稳定,所以接口不应依赖于实现
2)更好的灵活性:一旦接口模块化,很容以进行扩展。
3)更好的模块化;
另:应当为接口实现一个公共权限的虚析构函数。

第37条
公用继承代表可替换性(子类替换父类), 继承不是为了复用(指子类复用父类代码),是为了被复用(指父类让子类复用其接口)。

第38条
安全的改写
当改写一个虚函数时,保持可替换性,具体而言,弄清基类中此函数的前置和后置条件。不要修改虚函数中的默认参数,喜欢显示的声明virtual。

第39条
考虑将虚拟函数声明为非公用的, 将公用函数声明为非虚拟的

第40条
避免提供隐式转换
如何避免:
1)对于单参构造函数使用explicit关键字
2)使用有名函数而不是类型转换操作符来提供类型转换

第41条
将数据成员私有化,除了是C风格的struct类型。

第42条
不要暴露内部数据
这一条的最大意思在于如果通过公用接口返回内部数据,那么当这个内部数据是指针或是句柄(如文件描述符)时,可能用户在使用时,指针或文件描述符已经处于无效的状态。

// eg:#include <iostream>using namespace std;class A{public:    int *GetData () const { return pri_data; }private:    int *pri_data;};int main (void){    A *pa = new A;    int *data = pa->GetData();    delete pa;     *data = 3;    return 0;}

如果用户通过GetData接口获取到了私有数据指针,那么当用户使用这个指针的时候,指针已经失效了。

近期比较堕落,甚至到了颠覆自己状态的地步

第43条
明智的使用Pimpl
这个条目的主要内容是说如果一个类中包含另一个类类型的成员,最好的情况是包含指向这个类成员的指针。

第44条
倾向于写非成员和非友元函数

0 0