设计风格
第5条:一个实体应该只有一个紧凑的职责
摘要:一次只解决一个问题:只给一个实体(变量、类、函数、名字空间、模块和库)赋予一个定义良好的职责。随着实体变大,其职责范围自然也会扩大,但是职责不应该发散。
对于string的抱怨见于Sutter先生的《Exceptional C++ Style》,其中将string做了完全的解构,非常精彩。
事物往往是由小变大的发展。但是,到了一定的程度就需要有一些措施,比如公司大了,就需要改变管理的架构;程序库大了就需要重构。这样的过程一定是痛苦的,但如果为了避免短痛而无限制的拖下去,这个摊子就烂掉了。
第6条:正确、简单和清晰第一
摘要:软件简单为美:正确优于速度、简单优于复杂、清晰优于机巧。
避免使用程序设计语言中的冷僻特性。应该使用最简单的有效技术。对这一点我还算比较了解。我已经学会了很多的技巧,但通常在代码设计中,我尽量避免使用技巧,最简单平实的代码才是最好的代码。很多的技巧容易导致代码可读性变得很差。
第7条:编程中应该知道何时和如何考虑可伸缩性
摘要:小心数据的爆炸性增长。
对我来说,我设计的原则通常原因拿空间来换时间,好在存储的增长很快,没有在这一方面给我带来麻烦,值得我警醒的是我对程序的可伸缩性考虑还不够。
第8条:不要进行不成熟的优化
摘要:拉丁谚语云,快马无需鞭策。优化的第一原则是不要优化,第二原则是还是不要优化。再三测试,而后优化。
对于大的系统来说,优化并不是一件简单的事情,需要考虑的因素很多,CPU、内存、磁盘I/O、网络状况……,在这之间的复杂关系没有梳理清楚之前,进行优化,是一件很不安全的事情。事实上,优化过后有大量的程序需要测试需要做。未必优化完成后,这些后续工作可以跟的上,所以在进行优化之前还是需要多多慎重。
第9条:不要进行不成熟的劣化
摘要:放松自己,轻松编程。
在可以使用标准库的情况下,优先使用标准库。我想自己实现的算法,并不能比世界上最优秀的专家强,并且写出清晰的,可读性很强的代码,对程序是非常有帮助的。
第10条:尽量减少全局和共享数据
摘要:共享数据会导致冲突;避免共享数据,尤其是全局数据,共享数据会增加耦合度,从而降低可维护性,通常还会导致降低性能。
对此条,我深表赞同,过多的成员变量往往使程序变得非常难以阅读,这还会带来多线程同步的问题,而且使程序之间的耦合变得非常夸张。
记得VC6的string么?引用计数带来的好处远远不足以抵偿同步带来的效率损失。
第11条:隐藏信息
摘要:不要泄密:不要公开提供抽象的实体的内部信息。
尽量以接口的方式提供数据,这样的好处是如果对数据操作的要求有所改变的时候,只需要很少的修改。我基本可以做到将数据隐藏,但对于线程安全的容器,现在的相关工作还比较少,前几天出现的问题,给了我一个教训——即使是线程安全的容器,也难保哪天需要更换容器类型。
第12条:懂得何时和如何进行并发性编程
摘要:Thread safely
C++目前的标准并没有加上线程的概念,也就是说,所有的线程相关的工作都是平台相关的,我们希望下一代C++标准中出现线程的身影,虽然说现在ACE之类的扩展开源库也够用了。
如果需要线程安全,需要了解平台的特性,了解线程的操作和同步化原语。使用C++对平台相关性进行抽象,这部分的工作量相当大,但很多开源库已经做了,推荐使用ACE。保证非共享对象的独立,在多线程程序中,我觉得引用计数是一项双刃剑,需要酌情处理,例如VC6的std::string。加锁的时候,需要注意锁的释放,特别是中途返回和异常退出。加锁的时候需要考量加锁的策略。内部加锁将工作放在了调用者一端,调用者需要对共享对象比较了解,而适当的加以串行化。外部加锁,将锁封装在内部,对外提供线程安全的接口,例如线程安全的容器。在内部加锁的过程中,需要考虑加锁的范围、粒度和线程安全性。对操作的相关性要特别注意。对于大多数数据,应该是不加锁的,特别是一些从来不会修改的数据和从来不会跨线程操作的数据。
在进行加锁的同时,不可忽略的是加锁所带来的消耗。如果一旦线程安全的代价过大,就需要考虑修改设计了。
第13条:确保资源为对象所拥有。使用显式的RAII和智能指针
摘要:如果有能力操纵利器,没有道理不用。RAII(resource acquisition is intialization)
有什么可说呢?参考ACE的
ACE_Guard的加锁惯用法。构造函数和析构函数是Stroustrup给我们的礼物。