Bjarne Stroustrup 对C++程序员的忠告

来源:互联网 发布:linux版本发布时间 编辑:程序博客网 时间:2024/04/30 10:50

转自:http://blog.csdn.net/adm_qxx/archive/2007/05/20/1617488.aspx

1 致读者

[1] 在编写程序时,你是在为你针对某个问题的解决方案中的思想建立起一种具体表示。让程序的结构尽可能地直接反映这些思想:

      [a] 如果你能把“它”看成一个独立的概念,就把它做成一个类。

      [b] 如果你能把“它”看成一个独立地实体,就把它做成某个类的一个对象。

      [c] 如果两个类有共同的界面,将此界面做成一个抽象类。

      [d] 如果两个类的实现有某些显著的共同东西,静这些共性做成一个基类。

       [e] 如果一个类是一种对象的容器,将它做成一个模板。

      [f] 如果一个函数实现对某容器的一个算法,将它实现为对一族容器可用的      模板函数。

      [g] 如果一组类、模板等互相之间有逻辑关系,将它们放进一个名字空间力。

[2] 在你定义一个并不是实现某个像矩阵或复数这样的数学对象的类时,或者定义一个低层地类型如链接表的时候:

    [a] 不要使用全局数据(使用成员)。

    [b] 不要使用全局函数。

    [c] 不要使用公用数据成员。

    [d] 不要使用友元,除非为了避免[a][c]

    [e] 不要在一个类里面放“类型域”;采用虚函数。

    [f] 不要使用在线函数,除非座位效果显著的优化。

 

2 C++概览

 

[1] 不用害怕,一切都会随着时间的推移而逐渐明朗起来。

[2] 你并不需要在知道了C++地所有细节之后才能写出好的C++程序。

[3] 请特别关注程序设计技术,而不是各种语言特征。

 

3 标准库概念

 

[1] 不要像重新发明车轮那样企图做每件事;去使用库。

[2] 不要相信奇迹;要理解你的库能做什么,它们如何做,它们做时需要多大代价。

[3] 当你遇到一个选择时,应该优先选择标准库而不是其他的库。

[4] 不要认为标准库对于任何事情都是最理想的。

[5] 切记#include你所用到的功能的头文件。

[6] 记住,标准库的功能定义在名字空间std中。

[7] 请用string,而不是char*

[8] 如果怀疑,就用一个检查区间范围的向量。

[9] vector<T>list<T>map<key, value>都比T[]好。

[10] 如果要向一个容器中添加一个元素,用push_back()back_insert()

[11] 采用对vectorpush_back(),而不是realloc()

[12] main()中捕捉公共的异常。

 

4 类型和声明

 

[1] 保持较小的作用域。

[2] 不要在一个作用域和它外围的作用域里采用同样的名字。

[3] 在一个声明中(只)声明一个名字。

[4] 让常用的和局部的名字比较短,让不常用的和全局的名字比较长。

[5] 避免看起来类似的名字。

[6] 维持某种统一的命名风格。

[7] 仔细选择名字,反映其意义而不是反映实现方式。

[8] 如果所用的内部类型表示某种可能变化的值,请用typdef为它定义一个有意义的名字。

[9] typedef为类型定义同义词,用枚举或类去定义新类型。

[10] 切记每个声明中都必须描述一个类型(没有隐式的int)。

[11] 避免有关字符值的不必要的假设。

[12] 避免有关整数大小的不必要假设。

[13] 避免有关浮点类型表示范围的不必要假设。

[14] 优先使用普通的int而不是short int或者long int

[15] 优先使用double而不是float或者long double

[16] 优先使用普通的char而不是signed char或者unsigned char

[17] 避免做出有关对象大小的不必要假设。

[18] 避免无符号算术。

[19] 应该带着疑问去看待从signedunsigned,或者从unsignedsinged的转换。

[20] 应该带着疑问去看待从浮点到整数的转换。

[21] 应该带着疑问其看待向较小类型的转换,如将int转换到char

 

5 指针、数组和结构

 

[1] 避免非平凡的指针算术。

[2] 当心,不要超出函数的界限去写。

[3] 尽量使用0而不是NULL

[4] 尽量使用vectorvalarrray而不是内部(C风格)的数组。

[5] 尽量使用string而不是以0结尾的char数组。

[6] 尽量少使用普通的引用参数。

[7] 避免void*,除了在某些低级代理。

[8] 避免在代码中使用非平凡的文字量(“神秘的数”)。相反,应该定义和使用

       各种符号常量。

 

6 表达式和语句

 

[1] 应尽量可能使用标准库,而不是其他的库和“手工打造的代码”。

[2] 避免过于复杂的表达式。

[3] 如果对运算符的优先级有疑问,加括号。

[4] 避免显式类型转换。

[5] 若必须做显式类型转换,提倡使用特殊强制运算符,而不是C风格的强制。

[6] 只对定义良好的构造使用T(e)记法。

[7] 避免带有无定义求值顺序的表达式。

[8] 避免goto

[9] 避免do语句。

[10] 在你已经有了去初始化某个变量的值之前,不要去声明它。

[11] 式注释简洁、清晰、有意义。

[12] 保持一致的缩进编排风格。

[13] 倾向于去定义一个成员函数operator new()去取代全局的operator new()

[14] 在读输入的时候,总应考虑病态形式的输入。

 

7 函数

 

[1] 质疑那些非const的引用参数;如果你想要一个函数去修改其参数,请使用指针或者返回值。

[2] 当你需要尽可能减少参数复制时,应该使用const引用参数。

[3] 广泛而一致地使用const

[4] 避免宏。

[5] 避免不确定数目的参数。

[6] 不要返回局部变量的指针或者引用。

[7] 当一些函数对不同的类型执行概念上相同的工作时,请使用重载。

[8] 在各种整数上重载时,通过提供函数去消除常见的歧义性。

[9] 在考虑使用指向函数的指针时,请考虑虚函数或模板是不是更好的选择。

[10] 如果你必须使用宏,请使用带有许多大写字母的丑陋的名字。

 

8 名字空间和异常

 

[1] 用名字空间表示逻辑结构。

[2] 将每个非局部的名字放入某个名字空间里,除了main()之外。

[3] 名字空间的设计应该让你能很方便地使用它,而又不会意外地访问了其他的无关名字空间。

[4] 避免对名字空间使用很短的名字。

[5] 如果需要,通过名字空间别名去缓和和长名字空间的影响。

[6] 避免给你的名字空间的用户添加太大的记法负担。

[7] 在定义名字空间的成员时使用namespace::member的形式。

[8] 只在转换时,或者在局部作用域里,才用using namespace

[9] 利用异常去松弛“错误”处理代码和正常处理代码之间的联系。

[10] 采用用户定义类型作为异常,不用内部类型。

[11] 当局部控制结构足以应付问题,不要使用异常。

 

9 源文件和程序

 

[1] 利用头文件去表示界面和强调逻辑结构。

[2] #include将头文件包含到实现有关功能的源文件里。

[3] 不要在不同编译单位里定义具有同样名字,意义类似但又不同的全局变量。

[4] 避免在头文件里定义非inline函数。

[5] 只在全局作用域或名字空间里使用#include

[6] 只用#include包含完整的定义。

[7] 使用包含保护符。

[8] #includeC头文件包含到名字空间里,以避免全局名字。

[9] 将头文件做成自给自足的。

[10] 区分用户界面和实现界面。

[11] 区分一般用户界面和专家用户界面。

[12] 在有意向用于非C++程序组成部分的代码中,应避免需要运行时初始化的非局部对象。

 

10

 

[1] 用类表示概念。

[2] 只将public数据(struct)用在它实际杀过那仅仅时数据,而且对于这些数据

       成员并不存在不变式的地方。

[3] 一个具体类型属于最简单的类。如果有用的话,就应该尽可能使用具体类型,

       而不要采用更复杂的阿里,也不要用简单的数据结构。

[4] 只将那些需要直接访问类的表示的函数作为成员函数。

[5] 采用名字空间,使类与其协助函数之间的关系更明确。

[6] 将那些不修改对象值的成员函数做成const成员函数。

[7] 将那些需要访问类的表示,但无须针对特定对象调用的成员函数做成static    成员函数。

[8] 通过构造函数建立起类的不变式。

[9] 如果构造函数申请某种资源,析构函数就应该释放一资源。

[10] 如果在一个类里有指针成员,它就要有复制操作(包括复制构造函数和复    制赋值)。

[11] 如果在一个类里有引用成员,它就可能需要有复制操作(包括复制构造函    数和复制赋值)。

[12] 如果一个类需要复制操作或析构函数,它多半还需要有构造函数、析构函    数、复制赋值函数和复制构造函数。

[13] 在复制赋值函数里需要检查自我赋值。

[14] 在写复制构造函数时,请小心地复制每个需要复制的元素(当心默认的初    始式)。

[15] 在向某个类中添加新成员函数时,一定要仔细检查,看是否存在需要更新    的用户定义构造函数,以使它能够初始化新成员。

[16] 在类声明中需要定义整型常量时,请使用枚举。

[17] 在构造全局的和名字空间的对象时,应避免顺序依赖性。

[18] 用第一次开关去缓和顺序依赖性问题。

[19] 请记住,临时对象将在建立它们的那个完整表达式结束时销毁。

 

11 运算符重载

 

[1] 定义运算符主要是为了模仿习惯使用方式。

[2] 对于大型运算对象,请使用const引用参数类型。

[3] 对于大型的结果,请考虑优化返回方式。

[4] 如果默认复制操作对一个类和合适,最好是直接用它。

[5] 如果默认复制操作对一个类不和合适,重新定义它,或者禁止它。

[6] 对于需要访问表示的操作,优先考虑作为成员函数而不是作为非成员函数。

[7] 对于不访问表示的操作,优先考虑作为非成员函数而不是作为成员函数。

[8] 用名字空间将协助函数与“它们的”类关联起来。

[9] 对于对称的运算符采用非成员函数。

[10] ()作为多维数组的下标。

[11] 将只有一个“大小参数”的构造函数做成explicit

[12] 对于非特殊的使用,最好是用标准string而不是你自己的练习。

[13] 要注意引进隐式转换的问题。

[14] 用成员函数表达那些需要左值作为其左运算对象的运算符。

 

12 派生类

 

[1] 避免类型域。

[2] 用指针和引用避免切割问题。

[3] 用抽象类将设计的中心集中到提供清晰的界面方面。

[4] 用抽象类是界面最小化。

[5] 用抽象类从界面中排除实现细节。

[6] 用虚函数是新的实现能够添加进来,又不会影响用户代码。

[7] 用抽象类去尽可能减少用户代码的重新编译。

[8] 用抽象类是不同的实现能够共存。

[9] 一个有虚函数的类应该有一个虚析构函数。

[10] 抽象类通常不需要构造函数。

[11] 让不同概念的表示也不同。

 

13 模板

 

[1] 用模板描述需要使用到许多参数类型上去的算法。

[2] 用模板表述容器。

[3] 为指针的容器提供专门化,以减小代码规模。

[4] 总是在专门化之前声明模板的一般形式。

[5] 在专门化的使用之前先声明它。

[6] 尽量减少模板定义对于实例化环境的依赖性。

[7] 定义你所声明的每一个专门化。

[8] 考虑一个模板是否需要有针对C风格字符串和数组的专门化。

[9] 用表述策略的对象进行参数化。

[10] 用专门化和重载为同一概念的针对不同类型的实现提供统一界面。

[11] 为简单情况提供简单界面,用重载和默认参数去表述不常见的情况。

[12] 在修改为通用模板之前,在具体实例上排除程序错误。

[13] 如果模板定义需要在其他编译单位里访问,请记住写export

[14] 对大模板和带有非平凡环境依赖性的模板,应采用分开编译的方式。

[15] 用模板表示转换,但要非常小心地定义这些转换。

[16] 如果需要,用constraint()成员函数给模板的实参增加限制。

[17] 通过显式实例化减少编译和连接时间。

[18] 如果运行时的效率非常重要,那么最好用模板而不是派生类。

[19] 如果增加各种变形而又不重新编译是很重要的,最好用派生类而不是模板。

[20] 如果无法定义公共的基类,最好用模板而不是派生类。

[21] 当有兼容性约束的内部类型和结构非常重要时,最好用模板而不是派生类。

 

14 异常处理

 

[1] 用异常做错误处理。

[2] 当更局部的控制机构足以应付时,不要使用异常。

[3] 采用“资源申请即初始化”技术去管理资源。

[4] 并不是美国程序都要求具有异常时的安全性。

[5] 才用“资源申请即初始化”技术和异常处理器去维持不变式。

[6] 尽量少用try块,用“资源申请即初始化”技术,而不是显式的处理器代码。

[7] 并不是美国函数都需要处理每个可能的错误。

[8] 在构造函数里通过抛出异常指明出现失败。

[9] 在从赋值中抛出异常之前,式操作对象处于合法状态。

[10] 避免从析构函数里抛出异常。

[11] main()捕捉并报告所有的异常。

[12] 使正常处理代码和错误处理代码相互分离。

[13] 在构造函数里抛出异常之前,应保证释放在此构造函数里申请的所有资源。

[14] 使资源管理具有层次性。

[15] 对于主要界面使用异常描述。

[16] 当心通过new分配的内存在发生异常时没有释放,并由此而导致存储的流失。

[17] 如果一函数可能抛出某个异常,就应该假定它一定会抛出这个异常。

[18] 不要假定所有异常都时由excepion类派生出来的。

[19] 库不应该单方面终止程序。相反,应该抛出异常,让调用者去做决定。

[20] 库不应该生成面向最终用户的错误信息。相反,它应该抛出异常,让调用    者去做决定。

[21] 在设计的前期开发出一种错误处理策略。

 

15 类层次结构

 

[1] 利用常规的多重继承表述特征的合并。

[2] 利用多重继承完成实现细节与界面分离。

[3] virtual基类表达在类层次结构里对某些类(不是全部类)共同的东西。

[4] 避免显式的类型转换(强制)。

[5] 在不可避免地需要漫游类层次结构的地方,使用dynamic_cast

[6] 尽量使用dynamic_cast而不是typeid

[7] 尽量使用private而不是protected

[8] 不要声明protected数据成员。

[9] 如果某个类定义了operator delete(),它也应该有虚析构函数。

[10] 在构造和析构期间不要调用虚函数。

[11] 尽量少用为解析成员名而写的显式限定词,最好时在覆盖函数里用它。

 

16 库组织和容器

 

[1] 利用标准库功能,以维持可移植性。

[2] 决不要另行定义标准库的功能。

[3] 决不要认为标准库比什么都好。

[4] 在定义一种新功能时,应考虑它是否能纳入标准库所提供的框架中。

[5] 记住标准库功能都定义在名字空间std里。

[6] 通过包含保准卡头文件声明其功能,不要自己另行显式声明。

[7] 利用后续抽象的优点。

[8] 避免肥大的界面。

[9] 与自己写按照反向顺序的显式循环相比,最好是写利用反向迭代器的算法。

[10] base()reverse_iterator抽取出iterator

[11] 通过引用传递容器。

[12] 用迭代器类型,如list<char>::iterator,而不要采用索引容器元素的指针。

[13] 在不需要修改容器元素时,使用const迭代器。

[14] 如果希望检查访问范围,请(直接或间接)使用at()

[15] 多用容器和push_back()resize(),少用数组和realloc().

[16] vector改变大小之后,不要使用指向其中的迭代器。

[17] 利用reserve()避免使迭代器非法。

[18] 在需要的时候,reserve()可以使执行情况更容易预期。

 

17 标准库容器

 

[1] 如果要用容器,首先考虑用vector

[2] 了解你经常使用的每个操作的代价(复杂性,大O度量)。

[3] 容器的界面、实现和表示使不同的概念,不要混淆。

[4] 你可以依据多种不同准则去排序和搜索。

[5] 不要用C风格的字符串作为关键码,除非你提供了一种适当的比较准则。

[6] 你可以定义这样的比较准则,使等价的但是不相同的关键码值映射到同一个  关键码。

[7] 在插入和删除元素时,最好时使用序列末端的操作(back操作)。

[8] 当你需要在容器的前端或中间做许多插入和删除时,请用list

[9] 当你主要通过关键码访问元素时,请用mapmultimap

[10] 尽量用最小的操作集合,以取得最大的灵活性。

[11] 如果要保持元素的顺序性,选用map而不是hash_map

[12] 如果查找速度极其重要,选hash_map而不是map

[13] 如果无法对元素定义小于操作时,选hash_map而不是map

[14] 当你需要检查某个关键码是否在关联容器里的时候,用find()

[15] equal_range()在关联容器里找出所有具有给定关键码的所有元素。

[16] 当具有同样关键码的多个值需要保持顺序时,用multimap

[17] 当关键码本身就是你需要保存的值时,用setmultiset

 

18 算法和函数对象

 

[1] 多用算法,少用循环。

[2] 在写循环时,考虑是否能将它表述为一个通用的算法。

[3] 常规性地重温算法集合,看卡是不是能将新应用变得更明晰。

[4] 保证一对迭代器参数确实表述了一个序列。

[5] 设计时应该让使用最频繁的操作时简单而安全的。

[6] 吧测试表述成能够作为谓词使用的形式。

[7] 切记谓词是函数和对象,不是类型。

[8] 你可以用约束器从二元谓词做出一元谓词。

[9] 利用mem_fun()mem_fun_ref()将算法应用于容器。

[10] 当你需要将一个参数约束到一个函数上时,用ptr_fun()

[11] 切记srrcmp()0表示“相等”,与==不同。

[12] 仅在没有更特殊的算法时,才使用for_each()tranform()

[13] 利用谓词,以便能一各种比较准则和相等准则使用算法。

[14] 利用谓词和其他函数对象,以使标准算法能用于表示范围广泛的意义。

[15] 运算符<==在指针上的默认意义很少适用于标准算法。

[16] 算法并不直接为它们的参数序列增加或减少元素。

[17] 应保证用于同一个序列的小于和相等谓词相互匹配。

[18] 有时排好序的序列用起来更有效且优雅。

[19] 仅为兼容性而使用qsort()bsearch()

 

19 迭代器和分配器

 

[1] 在写一个算法时,设法确定需要用哪种迭代器才能提供可接受的效率,并(只

       )使用这种迭代器所支持的操作符去表述算法。

[2] 当给定的迭代器参数提供了多于算法所需 的最小支持时,请通过重载为该    算法提供效率更高的实现。

[3] 利用istream_traits为不同迭代器类别描述适当的算法。

[4] 记住在istream_iteratorostream_iterator的访问之前使用++

[5] 用插入器避免容器溢出。

[6] 在排错时使用额外的检查,后面只在必须时才删除这些检查。

[7] 多用++p,少用p++

[8] 使用未初始化的存储去改善那些扩展数据结构的算法性能。

[9] 使用临时缓冲区去改善需要临时数据结构的算法的性能。

[10] 在写自己的分配器之前三思。

[11] 避免malloc()free()realloc()等。

[12] 你可以通过为rebind所用的技术去模拟对模板的typedef

 

20

 

[1] 尽量使用string操作,少用C风格字符串函数。

[2] string作为变量或者成员,不作为基类。

[3] 你可以将string作为参数值或者返回值,让系统去关心存储管理问题。

[4] 当你希望做范围检查时,请用at()而不是迭代器或者[]

[5] 当你希望优化速度时,请用迭代器或[]而不是at()

[6] 直接或者间接地使用substr()去读字子串,用replace()去写子串。

[7] find()操作在string里确定值的位置(而不是写一个显式的循环)。

[8] 在你需要高效率地添加字符时,请在string的后面附加。

[9] 在没有极端时间要求情况下用string作为字符输入的目标。

[10] string::npos表示“sring的剩余部分”。

[11] 如果必要,就采用低级操作去实现极度频繁使用的strng(而不是到处用低   级数据结构)。

[12] 如果你使用string,请在某些地方捕捉length_errorout_of_rang异常、

[13] 小心,不要将带值0char*传递给字符串函数。

[14] 只是到必须做的时候,(再)用c_str()产生stringC风格表示。

[15] 当你需要知道字符串的类别时,用isalpha()isdigit()等函数,不要自己去

       写对字符值的检测。

 

21

 

[1] 在为用户定义类型的值定义<<>>时,应该采用意义清晰的正文表达形式。

[2] 在打印包含低优先级运算符的表达式时需要用括号。

[3] 在添加新的<<>>运算符时,你不必修改istreamostream

[4] 你可以定义函数,时其能基于第二个(或更后面的)参数,具有像virtual 数那样的行为。

[5] 切记,按默认约定>>跳过所有空格。

[6] 使用低级输入函数(如get()read())主要是为了实现高级输入函数。

[7] 在使用get()getline()read()时留心其终止准则。

[8] 在控制I/O时,尽量采用操控符,少用状态标志。

[9] (只)用异常去捕捉罕见的I/O错误。

[10] 联结用于交互式I/O的流。

[11] 使用哨位将许多函数的入口和出口代码集中到一个地方。

[12] 在无参数操控符最后不要写括号。

[13] 使用标准操控符式应记住写#include <iomanip>

[14] 你可以通过定义一个简单函数对象得到三元运算符的效果(和效率)。

[15] 切记,width描述只应用于随后的一个I/O操作。

[16] 切记precision描述只对所后所的浮点数输出操作有效。

[17] 用字符串流做内存里的格式化。

[18] 你可以描述一个文件流的模式。

[19] 在扩充I/O系统时,应该清楚地区分格式化(iostream)和缓冲(streambuf)。

[20] 将传输值的非标准方式实现为流缓冲。

[21] 将格式化值的非标准方式实现为流操作。

[22] 你可以利用一对函数隔离和封装其对用户定义代码的调用。

[23] 你可以在读入之前用in_avail()去确定输入操作是否会被阻塞。

[24] 划分清楚需要高效的简单操作和实现某种策略的操作(将前者做成inline  将后者做成virtual)。

[25] locale将“文化差异”局部化。

[26] sync_with_stdio(x)去混合C风格和C++风格的I/O,或者离解C风格和   C++风格的I/O

[27] 当心C风格I/O的类型错误。

 

22 数值

 

[1] 数值问题常常和微妙。如果你对数值问题的数学方面不是100%有把握,请   去找专家或者做试验。

[2] numberic_limits去确定内部类型的性质。

[3] 为用户定义的标量类型描述numberic_limits

[4] 如果运行时效率比对于操作和元素的灵活性更重要的话,那么请用valarray    去做数值计算。

[5] 用切割表述在数组的一部分上的操作,而不是用循环。

[6] 利用组合器,通过清除临时量和更好的算法来获得效率。

[7] std::complex做复数算术。

[8] 你可以把使用complex类的老代码通过一个typedef转为用str::complex模板。

[9] 在写循环从一个表出发计算某个值之前,先考虑一下accumulate()

       inner_produce()partial_sum()adjacent_difference()

[10] 最好使用具有特定分布的随机数,少直接用rand()

[11] 注意是你的随机数充分随机。

 

23 开发和设计

 

[1] 知道你试图达到什么目的。

[2] 心中牢记软件开发是一项人的活动。

[3] 用类比来证明是有意的欺骗。

[4] 保持一个特定的实实在在的目标。

[5] 不要试图用技术方式去解决社会问题。

[6] 在设计和对待人员方面都应该有长期考虑。

[7] 对于什么程序在编码之前先行设计是有意义的,在程序规模上并没有下限。

[8] 设计过程应鼓励反馈。

[9] 不要将做事情都当做取得了进展。

[10] 不要推广到超出了所需要的、你已有直接经验的和已经测试过的东西。

[11] 将概念表述为类。

[12] 系统里也存在一些不应该用类表述的性质。

[13] 将概念间的层次关系用类层次结构表示。

[14] 主动到应用和实现中去寻找概念间的共性,将由此得到的一般性概念表示    为基类。

[15] 在其他领域中的分类方式未必适合作为应用中的继承模型的分类方式。

[16] 基于行为和不变式设计类层次结构。

[17] 考虑用例。

[18] 考虑用CRC卡。

[19] 用现存系统作为模型、灵感的源泉和出发点。

[20] 意识到视觉图形工程的重要性。

[21] 在原型成为负担时就抛弃它。

[22] 为变化而设计,将注意力集中到灵活性、可扩展性、可移植性和重用。

[23] 将注意力集中到组件设计。

[24] 让每个界面代表在一个抽象层次中的一个概念。

[25] 面向变化进行设计,以求得稳定性。

[26] 通过将广泛频繁使用的界面做得最小、最一般和抽象来使设计稳定。

[27] 保持尽可能小,不为“特殊需要”增加新特征。

[28] 总考虑类的其他表示方式。如果不可能有其他方式,这个类可能就没有代    表某个清晰的概念。

[29] 反复评审、精化设计和实现。

[30] 采用那些能用于调试,用于分析问题、设计和实现的最好工具。

[31] 尽早、尽可能频繁地进行试验、分析和测试。

[32] 不要忘记效率。

[33] 保持某种适合项目规模的规范性水平。

[34] 保证有人负责项目的整体设计。

[35] 为可重用组件做文档、推介和提供支持。

[36] 将目标与细节一起写进文档里。

[37] 将为新开发者提供的教许材料作为文档的一部分。

[38] 鼓励设计、库和类的重用,并给予回报。

 

24 设计和编程

 

[1] 应该向数据抽象和面向对象设计的方向发展。

[2] (仅仅)根据需要去使用C++的特征和技术。

[3] 设计应与编程风格相互匹配。

[4] 将类/概念作为设计中最基本的关注点,而不是功能/处理。

[5] 用类表示概念。

[6] 用继承(仅仅)表示概念间的层次结构关系。

[7] 利用应用层静态类型的方式给出有关界面的更强的保证。

[8] 使用程序生成器和直接界面操作工具去完成定义良好的工作。

[9] 不要去使用那些与任何通用程序设计语言之间都没有清晰界面的程序生成      器或者直接界面操作工具。

[10] 保存不同层次的抽象相互分离。

[11] 关注组件设计。

[12] 保证虚函数有定义良好的意义,每个覆盖函数都实现预期行为。

[13] 公用界面表示的是“是一个”关系。

[14] 成员表示的是“有一个”关系。

[15] 在表示简单包容时最好用直接成员,不用指向单独分配的对象的指针。

[16] 设法保证使用依赖关系为易理解的,尽可能不出现循环,而且最小。

[17] 对于所有的类,定义好不变式。

[18] 显式地将前条件、后条件和其他断言表述为断言(可能使用Assert())。

[19] 定义的界面应该只暴露初尽可能少的信息。

[20] 尽可能减少一个界面对其他界面的依赖性。

[21] 保持界面为强类型的。

[22] 利用应用层的类型来表述界面。

[23] 将界面表述得使请求可以传递给远程得服务器。

[24] 避免肥大的界面。

[25] 尽可能地使用private数据和成员函数。

[26] protected/private区分开派生类的设计者与一般用户间的不同需要。

[27] 使用模板去做通用型程序设计。

[28] 使用模板去做算法策略的参数化。

[29] 如果需要在编译时做类型解析,情使用模板。

[30] 如果需要在运行时做类型解析,请使用层次结构。

 

25 类的作用

 

[1] 应该对一个类的使用方式做出有意识的决策(作为设计师或者作为用户)。

[2] 应注意到涉及不同种类的类之间的权衡问题。

[3] 用具体类型去表示简单的独立概念。

[4] 用具体类型去表示那些最佳效果及其关键的概念。

[5] 不要从具体类派生。

[6] 用抽象类去表示那些对象的表示可能变化的界面。

[7] 用抽象类去表示那些可能出现多种对象表示共存情况的界面。

[8] 用抽象类去表示现存类型的新界面。

[9] 当类似概念共享许多实现细节时,应该使用结点类。

[10] 用结点类去逐步扩充一个实现。

[11] 用运行时类型识别从对象获取界面。

[12] 用类去表示具有与之关联的状态信息的动作。

[13] 用类去表示需要存储、传递或者延迟执行的动作。

[14] 利用界面类去为某种新的用法而调整一个类(不修改这个类)。

[15] 利用界面类增加检查。

[16] 利用句柄去避免直接使用指针和引用。

[17] 利用句柄去管理共享的表示。

[18] 在那些能预先定义控制结构的应用领域中使用应用框架。

 

 

 

原创粉丝点击