《《C++编程风格》学习笔记.pdf》由会员分享,可在线阅读,更多相关《《C++编程风格》学习笔记.pdf(4页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、C+编程风格(C+Programming Style)(美)Tom Cargill 著聂雪军 译第 0 章 概述由于内联函数的展开所增加的代码量甚至可能降低程序的执行速度,因为代码量的增加有可能妨碍有效的代码缓冲操作。只有当内联函数确实能够带来程序性能的提升时,才应该被用在代码中。第 1 章 抽象类实现的函数代码不应该显式地依赖于组件是否存在,而应该对每种组件进行统一的处理。(自己的理解:在类层次中,处于相同层次上的派生类,应该使他们的行为一直,这样便于对各个派生类进行统一处理)将共同的抽象提取出来并放到基类中。通常来说,对象的行为是指它对外部激励的反应。如果从对外部激励反应的角度来考察一个对
2、象,我们强调的是对象的独立性在程序的执行期间,每个对象都是程序的一个自治实体。最普遍的外部激励形式就是成员函数的调用,而对象将通过执行成员函数来作出反应,或者是完成某个功能,或者是返回一个值,或者二者都有。多态虚函数可以使得不同类型的对象对相同的外部激励产生不同的反应。一个类应该能够描述一组对象。如果派生类之间的区别在于 属性,则用数据成员来表示;如果在于行为,则用虚函数来表示。使用数据成员来表示属性之间的区别,要比使用虚函数的表示更加易于编程。如果通过公有继承来产生派生类,那么这个派生类应该是其基类的特化。将派生类之间的不同之处局限在初始化过程中(构造函数的特化)是一种使用继承的正确方法。程
3、序员们不应该只是因为C+提供了更为复杂的编程技术而放弃那些标准的和可靠的编程技术。多态并不是所有程序设计问题的解决方案。第 2 章 一致性设计一个类时,程序员通常需要从类的接口和类的实现两个方面来考虑,接口必须能够代表一致的抽象,而实现则必须使得对象在行为上与这个抽象保持一致。接口和实现可以通过不同的模型来表示对象状态,这也分别称之为逻辑状态和物理状态。逻辑状态模型通常是物理状态模型的简化,多个物理状态可以对应于一个逻辑状态。构造函数应似的对象处于明确定义的状态。通常来说,用默认参数的形式来代替函数重载的形式,可以使得程序更加易于维护。即我们应该考虑使用默认参数的形式来代替函数重载的形式。对于
4、每个类,我们都可以写出一组类不变性(class invariant)条件,在类的每个对象的生存期内,这些条件都应该是成立的。我们首先在构造函数中建立起类不变性,然后在其他成员函数中维持这个不变性,这样就在对象的整个生存期内都保持了类不变性。因此,用一致的方式来定义对象的状态这需要识别出类不变性。(可以将类不变性作为注释,写在类的定义中)类的接口定义应该是一致的避免产生困惑。避免对从不使用的状态信息进行计算和存储。即只有当信息在后续操作中需要被用到时候,才应该被存储。在定义operator=时,我们要注意 x=x 这种情况第 3 章 不必要的继承如果基类的析构函数没有被声明为虚函数,我们动态创建
5、一个派生类对象,并通过基类类型的指针来删除这个对象时,将只会调用基类的析构函数。私有继承不但能够防止基类的公有接口成为派生类公有接口的一部分,还能够防止将基类型的指针或者引用指向派生类的对象。任何对私有基类成员的访问都是非法的,同样,将任何私有基类型的指针或者引用指向派生类对象也是非法的。在私有基类中,派生类继承了所有的实现,但没有继承任何接口。而在继承公有的抽象基类时,派生类继承了所有的接口,但所继承的实现可能是不完整的或者是不存在的。我们要识别出对实现的继承;可以使用私有基类或者(更好的方法是)使用成员对象(即将私有基类作为派生类的一个数据成员)。第 4 章 虚函数类设计中的一致性:(1)
6、在接口上保持外部一致性(2)在每个对象状态的实现上保持内部一致性(3)在使用继承时,在基类以及派生类接口之间的一致性。派生类在处理继承而来的状态时,必须与基类保持一致。如果在公有基类中没有定义虚析构函数,那么在所有的派生类或者派生类的数据成员中都应该没有定义析构函数。(在多重继承时,这条规则将是不充分的)通常情况下,公有基类的析构函数应该被声明为虚函数。基类所描述的应该是基类对象和派生类对象中共同抽象的所有属性。如果在基类中包含了共同的状态和行为,那么一个简单的描述就可以服务于许多的派生类。如果我们没有识别出共同的属性并将它们放到基类中,那么这些属性在每个派生类中都将被重复定义。这种重复性将使
7、得程序难以进行编写、阅读和维护。类的设计原则:降低耦合性将类之间的交互最小化数据与函数:通常,把信息记录在数据中比记录在函数中要更为简单,并且非虚的函数要比虚函数更加容易理解。当我们要决定在一个类中到底是使用数据成员还是函数成员来表示一个特性时,我们首先应该考虑:这个特性是由属性值来描述的还是由行为来描述的?如果这个特性是一个属性值,那么用数据成员来表示就更简单。派生类对象将继承这个数据成员,并可以使用它的值。如果这个特性是一种行为(一种操作或者算法),那么我们应该用成员函数来表示它。这个成员函数可以是虚汗素,也可以是非虚函数。因此,对于在继承层次中需要由类的行为来描述的特性,我们还要进一步地
8、提出问题:派生类和基类是否有着同样的行为?如果派生类必须表现出不同的行为,那么就应该使用虚函数,这样每个派生类都可以给出自己的实现如果所有的派生类都表现出相同的行为,那么它们就可以共享一个在基类中定义的非虚函数。没有哪个类是完美的;过窄的设计要好于过宽的设计。(这个规则基于的假设是:所有相关的类都在程序员的控制之中)第 5 章 运算符重载运算符重载不能违背我们已知的语法。重载运算符的含义必须是自然的,而不是为了展示程序员的聪明。重载运算符必须能够与其他的运算符进行正确的交互。要保持重载运算符的行为是一致的。(例,在重载和b 和 ba 这样的表达式是等价的)重载运算符时,应该保持一组相关运算符的
9、完整性。(许多运算符是一个相关运算符的集合,如果对这个运算符集合中的某个进行了重载,那么我们应该很自然地去重载其他运算符)定义运算符=时,我们要注意 x=x 这种特殊的情况在对运算符进行重载时,要避免产生困惑。第 6 章 包装我们需要知道从函数中返回的指针的有效生存期。独立的对象应该有独立的行为。即各个独立的对象之间不会相互改变各自的状态。不要对某些基本的信息进行完全的封装我们要使得这些信息可以通过某些方法来进行访问。用可靠的方法来处理失败。对于一个对象来说,最好的行为是:对象知道已经发生了错误,然后对象将保持自身的一致性并且表现出预期的行为而不是未定义的行为。在发生错误时,对象的行为应该是明
10、确定义的。第 7 章 效率在程序的源代码中,我们不一定能看到所有的对象。例如,编译器可能会传见临时对象来作为函数参数。那些需要临时对象的语法规则将使得我们难以准确的估计执行开销。因此,一些看上去不错的源代码可能会被编译成执行开销高昂的机器代码。我们不能通过主观臆测就作出判断,而是应该通过执行性能分析的结果来找出问题所在。我们需要对类的实现进行分析,以找出性能问题的根源。完整的接口有助于实现高效的客户代码。在我们找出关于性能瓶颈的信息之前,不去对效率进行考虑。许多效率的问题只有当类在实际环境中被使用时才会暴露出来。这就意味着,我们在最开始设计类的接口时应该尽量简单,然后随着对接口的不断使用而逐步
11、增加接口中的函数。第 8 章 案例研究我们始终要记住结束空字符的存在正确的表达式应该是 new charstrlen(s)+1不要使用构造函数来初始化静态数据成员在理想的情况下,在设计良好的软件组件中应该只有一个明确定义的功能,这将在整个组件中形成一种内聚的形式。每个类都应该只有一个唯一的、内聚的功能。如果我们可以很容易的创建一个ADT,那么我们就没有理由将一个类设计成模块的形式。如果使用了ADT,那么我们就可以在需要的时候才进行实例化。我们应该将类设计成抽象数据类型而不是模块类。在计算机程序中,我们可以有两种基本的方法来表示映射:一张表或者一个算法,用 C+的术语来说就是被动数据(passi
12、ve data)或者可执行代码(executable code)如果泛化的情形同样简单时,那么我们最好不要只实现某种具体的情形。第 9 章 多重继承访问控制(无论成员是公有的还是私有的)并不会影响编译器在作用域规则下对标识符的搜索方式。当派生类能够在继承层次结构中通过多条途径与同一个基类相关联时,就会带来进一步的二义性。产生这种现象的原因是由于在派生类的多个基类中有可能又有着共同的基类。虚基类将使得在每个派生类对象中只有唯一的基类部分,而无论有多少条路径可以到达基类。从语义上来说,我们可以在派生类的声明中,在基类的名字前面加上关键字 virtual 来表示使用虚基类。这里的关键字virtual 和我们所熟悉的在成员函数原型中使用的 virtual 是不相干的。我们要避免人为地将一个类限制为只能被用作为基类。如果在多重继承的层次结构中存在着析构函数,那么每个基类的析构函数都应该是虚函数。如果在客户对象中需要包含某种服务,那么我们应该使用成员对象,而不是继承。第 10 章 规则总结该章是将前面章节总结的规则,全部罗列了一边。在本文档中,已经分散在前面部分了。