9.2 接口继承
多重继承中毫无争议的一种运用属于接口继承(interface inheritance)。在C++中,所有的继承都是实现继承(implementation inheritance),因为在一个基类、接口和实现中的任何内容都将成为派生类的一部分。只继承一个类的某些部分(比如只继承接口)是不可能的。就像第1卷第14章说明的那样,当客户使用派生类的对象时,私有的和被保护的继承将可能限制派生类成员对基类成员的访问,但是这些并不会影响派生类;它仍然包含了所有的基类数据,并且可以访问所有的非私有的基类成员。
另一方面,接口继承仅仅是在一个派生类接口中加入了成员函数的声明(declaration),在C++中并不直接支持这种使用方法。C++中模拟接口继承常见的技术是从一个仅包含声明(没有数据和函数体)的接口类(interface class)派生一个类。除了析构函数以外,这些声明都是纯虚函数。举例如下:
类Able“实现”了接口Printable、Intable和Stringable,因为它提供了那些对它们进行声明的函数的实现。因为Able派生自所有这3个类,Able对象具有多“is-a”关系。例如,对象a的行为可能像一个Printable对象,因为它的类Able公有派生自Printable,并且提供了对print()的实现。测试函数并不需要知道作为参数使用的大多数的派生类对象的类型;它仅仅需要这样一个可以代替它们的参数类型的对象。
一般来说,采用模板来解决问题的方法将会使程序变得更加简洁:
Printable、Intable和Stringable这些名字现在仅是模板参数,这些参数假设在各自的语境中表示存在的操作。换句话说,测试函数可以接受任何一种类型的参数,这些参数类型与正确的识别标志和返回类型一起提供了一个成员函数的定义;这些参数并不必要派生自一个共同的基类。有些人更适应第1种版本的示例,因为类型名可以通过继承关系保证能够确定,该继承关系由预期的接口来实现。而其他人更满足于这样一个事实,如果提供的模板类型参数不能满足测试函数所需要的操作,该错误在编译时仍然会被捕获。对比前一个方法(继承),后面的方法是一种“较弱”的类型检查形式,但是对程序员(和程序)来说,效果是相同的。这就是被许多现代C++程序员所接受的弱输入检查的一种形式。