9.8 扩充一个接口
多重继承最好的应用之一,涉及由第3方提供的脱离了程序员控制的代码。假设获得了这样一个库,它由一个头文件和一些编译好的成员函数组成,但没有这些成员函数的源代码。这个库是一个带有虚函数的类层次结构,并且包含一些能将指针指向类库中基类的全局函数;就是说,它使用了这些库对象的多态性。现在,假设使用这个库创建一个应用程序并利用基类的多态性编写了程序员自己的代码。
在软件项目开发的后期或在其维护期间,程序员可能发现由软件供应商提供的基类的接口并没有提供所需要的功能:提供的函数可能是非虚函数,但现在却需要它是个虚函数,或者接口中的虚函数完全地失效了,而该虚函数对于问题的解决却是至关重要的。使用多重继承可以解决这个问题。
例如,这里就是你得到的库的一个头文件:
假设这个库很大,由多个派生类和一个较大的接口组成。注意,它还包括了函数A()和B(),这些函数都有一个基类对象的引用,并且都能利用多态性对其进行处理。这里是库的实现文件:
一般很难在用户自己的软件项目中获得这个源代码。而获得的只不过是像Vendor.obj或Vendor.lib(或与使用的系统相配的文件后缀)这样编译好的文件。
问题发生在对这个库的使用中。首先,析构函数不是虚函数。[1]另外,f()没有被设计为虚函数;在这里,假定库的创造者决定它不需要是虚函数。用户还可能发现,作为基类的接口缺少解决问题所必要的函数。还可以假设用户已经编写了利用现有接口的一些代码(更不用说函数A()和B(),它们已经超出了用户的控制),并且不想修改它。
为了补救这个问题,用户可以创建一个自己的类接口,并且采用多重继承方法产生一组新的派生类,这些新派生类派生自用户创建的类接口和已存在的类:
在MyBase(它没有使用MI)中,f()和析构函数现在都是虚函数,并且在接口中加入了一个新的虚函数g()。现在,原始库中的每个派生类都必须重新创建,并在新的接口中利用MI混合。函数Paste1:v()和Paste1:f()只需要调用原始基类中的成员函数的版本。但是现在,如果将派生类对象向上类型转换为MyBase,就像在main()中:
任何通过mp执行的函数调用都将是多态的,包括delete。同样地,新的接口函数g()也可以通过mp来调用。下面是程序的输出结果:
原始的库函数A()和B()仍然能够照常工作(假设新函数v()调用了它的基类版本)。现在析构函数是virtual的,并且展现了正确的行为。
虽然这个例子有点儿混乱,但在实践中确实发生过,并且这个例子清晰地演示了哪里需要使用多重继承:必须能够将派生类向上类型转换为两个基类类型。
[1]人们已经在商品化的C++库中看到了这点,至少在一些早期的库中是这样。