条款34:区分接口继承和实现继承

1、public继承细分

实际上细分为:函数接口继承函数实现继承。这两种细分更像是函数声明和函数定义之间的差异。

从这两个角度出发,public继承可以分为:

  • 只继承接口
  • 同时继承接口和实现,且继承而来的实现能够被覆写
  • 同时继承接口和实现,且继承而来的实现不能够被覆写

2、三种继承对应的成员函数的写法

(1)只继承接口
  • 对应的成员函数的写法为:pure virtual
  • 特点:此类继承派生类必须自己写实现。

注意:pure virtual 函数是可以写定义的,但是只能通过类名调用。

(2)同时继承接口和实现,且继承而来的实现能够被覆写
  • 对应的成员函数的写法:virtual
  • 特点:此类继承派生类可以缺省实现,缺省的话,就会继承基类的实现。也可以自己手动写实现,这样相当于是覆盖了基类的实现。
(3)同时继承接口和实现,且继承而来的实现不能够被覆写

对应的成员函数的写法:non-virtual
特点:此类继承,培生累必须继承缺省和实现,且不饿能够修改。

3、继承缺省实现的优缺点

  • 继承缺省实现的好处是:提高了代码的重用性(即:相同性质的实现放在一个缺省实现中。)
  • 继承缺省实现的缺点是:容易使逻辑错误,即本该重写(特殊性质)实现的派生类,没有重写,而是继承了缺省实现。

4、第二种继承存在的问题

  • 缺省的实现,如果是所有派生类的共同性质,那么这样的继承缺省实现的派生类没有问题。
  • 但是,如果派生类的实现应该是与缺省实现不同的,但是却继承了缺省实现的话,这样就会引发编程逻辑错误

5、如何解决上述问题?

解决问题的思路是:使得virtual函数的接口和实现分离,但是有两种不同的手法。

(1)方案一:
  • 首先,将原本的impure virtual函数改写成:pure virtual
  • 将原本共同的性质抽取出来,写成一个函数,放在作用域:protected
  • 派生自此基类的类中,如果拥有该性质,可以在实现中直接调用抽取的函数。

方案一的缺点:过度雷同的函数名称可能引起class命名空间污染问题。

(2)方案二:

  • 首先,将原本的impure virtual函数改写成:pure virtual
  • pure virtual函数可以写定义的。将原本共同的性质抽取出来,写到该pure virtual函数的定义中
  • 派生自此基类的类中,如果拥有该性质,可以在实现中通过类名调用该pure virtual函数的定义。

6、接口与实现的继承写法容易犯的错误

(1)错误一:将所有函数声明为non-virtual
  • 将一个不作为基类使用的类的成员函数全部声明为non-virtual是合理的。
  • 一般打算用作基类的类,都是会有virtual函数的。

注意:关于virtual函数成本的考虑。参考,80-20法则。即:将将重心放在举足轻重的20%的代码上,这部分代码应该重点关注效率、成本问题。

(2)错误二:将所有函数声明为virtual
  • 对于接口类(interface classes),这样左是可以的。
  • 但是一个类如果并不打算作为接口类使用,某些函数应该明确写成non-virtual 函数,即不允许派生类修改(适用于:不变性为重心,特异性无关紧要的函数)。

总之,virtual函数主要考虑的是特异性,即派生类可能和基类拥有不同的性质的时候应该写成virtualnon-virtual则主要考虑的是不变形,即所有的派生类都应该拥有这一性质的话,就应该写成non-virtual.

posted @ 2019-12-19 16:06  江南又一春  阅读(183)  评论(0编辑  收藏  举报