C++ 特性和接口实现原则

来源:互联网 发布:微处理器和单片机 编辑:程序博客网 时间:2024/06/11 22:06

C++  三大特点:
      面向对象编程对于产出高质量,易维护的代码是非常有帮助的。面向对象编程的概念构建于三个基本特征之上:封装,继承,多态。在C++中,class是面向对象编程概念的核心和具体形式。class通过私有成员体现“封装”,通过直接继承或者组合体现“继承”,通过虚函数和动态绑定(dynamic binding)体现“多态”。重载也是一种多态,“编译期多态”!

C++ 类设计原则

class的设计质量直接决定了整个系统的质量。从整体功能层面谈class设计,有这么三条原则:
       1、单一功能原则(Single Responsibility Principle)
  一个class就其整体应该只提供单一的服务。如果一个class提供多样的服务,那么就应该把它拆分,反之,如果一个在概念上单一的功能却由几个class负责,这几个class应该合并。
  2、开放/封闭原则(Open/Close Principle)
  一个设计并实现好的class,应该对扩充的动作开放,而对修改的动作封闭。也就是说,这个class应该是允许扩充的,但不允许修改。如果需要功能上的扩充,一般来说应该通过添加新类实现,而不是修改原类的代码。添加新类不单可以通过直接继承,也可以通过组合。
  3、最小惊讶原理(Least Surprise Principle)
  在重载函数,或者子类实现父类虚函数时,应该基本维持函数原来所期望的功能。就在实现的时候,尽量保持子类实现的功能和基类预期。

接口特征:


接口具有以下特征:

  • 接口类(或结构)必须在命名空间内声明且可具有公共或私有可访问性。仅将公共接口发送到元数据。

  • 接口的成员可以包括属性、方法和事件。

  • 所有接口成员既是隐式公开的又是虚拟的。

  • 不允许字段和静态成员。

  • 用作属性、方法参数或返回值的类型只能是 Windows 运行时类型;这包括基本类型和枚举类类型。

http://technet.microsoft.com/zh-cn/library/hh755792
==================================================================================================================================

C++ 接口和实现 纯虚函数
  在系统中,观察一个class有两个角度,从外部或者用户角度我们看到的是接口,从内部我们看到的是实现。因为系统肯定要不断修改,因此实现免不了不停的变化,但是接口又被要求尽量保持稳定。这两者的矛盾必须通过良好的设计尽量避免,基本原则就是将实现细节与接口隔离。

下面列出几条比较具体点的:
         1、接口的设计保持最小而完整
  精简接口函数个数,使每一个函数有代表性,函数功能恰好覆盖class的职能。一个最小的接口可以使维护简单,增加潜在的代码重用性,减少客户的迷惑,并且也可以缩小头文件长度和编译时间。当改进函数时,应该用类似函数名实现改进而保留原函数,代码注释里应该有相应的说明。可以增加新函数,但不能删除旧函数。
  2、成员变量应该都为私有
  显而易见,public变量破坏封装性以及接口和实现的分离;protected变量也可能使客户编写继承类而依赖于父类的实现细节。
  3、避免函数返回成员变量的指针或引用
  这么做也会使客户代码依赖于实现细节。
  4、考虑是否禁用编译器缺省产生的函数
  这些函数包括:复制构造函数,赋值操作符(operator =)。如果我们不打算定义自己的版本而不禁用默认版本的话,可能使客户代码在不注意的情况下调用这些函数。当实现发生改动时就可能引起问题,比如 class多了一个heap memory指针。如果我们允许对象拷贝,比较稳妥的方法是禁用它们,而定义一个专门的clone()函数。

兼容性(compatibility)
  不用说,兼容性是非常重要的。Intel和Microsoft之所以如此成功,其中一个重要方面就是他们的产品,不管是硬件 还是软件,都做到了很好的兼容老产品。代码的兼容也是如此。难以想象,如果客户依赖于你的library产品,而要因为你的产品的更新而不断的重写他的代码,他还会继续用你的产品。
  代码兼容可以简单分为二进制兼容和源代码兼容。二进制兼容也就是说,客户的已编译代码可以在不用重新编译的情况下,直接使用你的不同版本的已编译代码。源代码兼容就是,如果你的代码更新了,客户的代码不需要修改,只需要重新编译就可正常运行。在C++中,接口一般是由头文件和library二进制代码提供,因此,任何可能造成library代码和旧的头文件不一致的情况都可能破坏二进制兼容,因为客户代码必须和新的头文件重新编译一次。


  因此,遵循几条准则可以使你更轻松地解决兼容性问题:
  1、不改变类的大小或者改变成员变量的顺序
  包括几个方面:不增加或减少成员变量;不修改成员变量类型;不改变成员变量的声明顺序;不改变虚函数的有无。显而易见,增加或减少成员变量会改变类的大小,并且需要更新头文件,从而可能造成与客户代码不兼容。类型的变化也可能引起类的大小的变化。成员变量的访问一般是由编译器按偏移量确定,顺序如果改变,偏移量也就会改变,破坏了二进制兼容。至于虚函数的有无,决定是否存在虚函数表指针,也就影响了类的大小和成员变量的顺序。
  2、不使用inline函数
  inline函数声明于头文件中,并且被编译于客户代码中,如果inline函数访问了private成员,该成员又改变了顺序,那么inline函数虚要被重新编译,破坏了二进制兼容。
  3、接口函数不使用虚函数
  虚函数的访问和成员变量类似,是通过虚函数表中的偏移。虚函数顺序的改变会影响偏移。因此,在条件允许时,应该避免使用public虚函数。比如:

class Picture {
 public:
  virtual void Draw();
};


  应该改为

class Picture {
 public:
  void Draw();
 private:
  virtual void DoDraw();
};

void Picture::Draw()


  4、不改变接口函数的顺序
  在很多嵌入式系统中,链接库通过输出函数表(exported function table)暴露接口以节省空间。此时,对接口函数的访问也是通过索引值进行,因此改变顺序也会破坏兼容性。
  5、避免使用函数缺省参数
  给函数形参设定缺省值可以方便客户,但是可能破坏兼容。缺省值随头文件给出,缺省值的改变也就会引起兼容问题。