如何让派生类使用基实现来满足接口
Posted
技术标签:
【中文标题】如何让派生类使用基实现来满足接口【英文标题】:How to have a derived class use the base implemention to satisfy an interface 【发布时间】:2011-10-12 15:48:57 【问题描述】:我有以下两个接口,它们不是继承层次结构的一部分。然后我有两个具体的类,一个派生自另一个。
class ICar
public:
virtual void drive() = 0;
;
class IFastCar
public:
virtual void drive() = 0;
virtual void driveFast() = 0;
;
class Car : public virtual ICar
public:
void drive() ;
;
class FastCar : public Car, public virtual IFastCar
public:
void driveFast() ;
;
int main()
FastCar fc;
fc.drive();
fc.driveFast();
return 0;
编译时出现以下错误:
error: cannot declare variable `fc' to be of type `FastCar'
error: because the following virtual functions are abstract:
error: virtual void IFastCar::drive()
如果我在 FastCar 中编写一个函数来将 drive() 委托给基类,我的程序就可以运行
void drive() Car::drive();
是否可以在不编写委托给基类的方法的情况下编译 FastCar?
注意:ICar 和 IFastCar 由两个不同的团队创建,位于两个不同的项目中。团队已就共享操作的通用方法签名达成一致。我在实现类中使用了继承来尝试重用实现中相同的部分。
【问题讨论】:
我认为 Herb Sutter 的 Multiple Inheritance, Part 3 在这里可能会有所帮助,以及另一个有类似问题的线程:***.com/questions/616380/… 在您的示例中,FastCar 继承了两个不相关的接口(iCar 和 iFastCar)。这两个接口都定义了一个不同的纯虚方法drive()。这是意图,还是应该 IFastCar 从 ICar 继承? 【参考方案1】:问题在于这里发生了多重继承......即使FastCar
派生自Car
,基类Car
中drive()
的版本只覆盖ICar::drive()
,而不是IFastCar::drive()
。因此,由于您从IFastCar
派生FastCar
,因此您需要在某些时候在FastCar
中定义一个函数,该函数还覆盖纯虚拟抽象方法IFastCar::drive()
...继承的Car::drive()
将不会自动覆盖IFastCare::drive()
,因为它不是从该类继承的。 IFastCar::drive()
和 ICar::drive()
是两个不同的纯抽象虚函数,需要分别重写。如果您想让IFastCar::drive()
的接口调用您继承的Car::drive()
函数,那么您需要像在FastCar::drive()
版本中所做的那样委托该继承函数,该版本专门调用其基类版本drive()
.
【讨论】:
【参考方案2】:问题是Car
没有实现IFastCar::drive
而只是ICar::drive
。第一个设计问题是IFastCar
为什么不扩展ICar
接口并重新定义相同的操作?
如果由于某种原因这不是一个选项,那么您可以做的最简单的事情实际上是通过将请求转发到 Car::drive
方法来在 FastCar
中实现 IFastCar::drive()
:
void FastCar::drive()
Car::drive();
【讨论】:
那行不通。我假设您的意思是FastCar
而不是 IFastCar
。【参考方案3】:
您在这里使用virtual
继承是虚假的,并且是转移注意力。 virtual
继承仅用于从公共基类派生一次; 不只为匹配的成员签名引入一个声明。
所以,您的 FastCar
具体类实际上有 3 个成员,而不是 2 个:
virtual void ICar::drive()
virtual void IFastCar::drive()
virtual void IFastCar::driveFast()
这两个不同的drive()
似乎是相关的,因此您的设计似乎存在缺陷。您不希望 virtual
继承 - 您可能希望 IFastCar
派生自 ICar
。
class ICar
public:
virtual void drive() = 0;
;
class IFastCar : public ICar
public:
virtual void driveFast() = 0;
;
class Car : public virtual ICar
public:
void drive() ;
;
class FastCar : public IFastCar
public:
void drive() ;
void driveFast() ;
;
int main()
FastCar fc;
fc.drive();
fc.driveFast();
return 0;
如果您希望Car
实现FastCar
将使用的一些基本功能,那么您可以从Car
派生FastCar
,这就是您想要@987654334 的原因@继承。请记住在菱形顶部正下方的位置应用virtual
继承:
class ICar
public:
virtual void drive() = 0;
;
class IFastCar : virtual public ICar
public:
virtual void driveFast() = 0;
;
class Car : public virtual ICar
public:
void drive() ;
;
class FastCar : public IFastCar, public Car
public:
void driveFast() ;
;
int main()
FastCar fc;
fc.drive();
fc.driveFast();
ICar* fc_a = new FastCar;
fc_a->drive(); // invokes Car::drive() via domination
return 0;
如果您编译并运行上述代码,您将获得所需的行为,但代价是编译器警告。在 MSVC10 上显示:
1>main.cpp(19): warning C4250: 'FastCar' : inherits 'Car::Car::drive' via dominance
1> main.cpp(13) : see declaration of 'Car::drive'
这是一个警告,您的实现继承架构可能被搞砸了。事实上,在大多数情况下它可能是(尽管在这种情况下不是)。这可能会很快变得非常混乱,特别是对于多年后试图找出您的代码的维护程序员来说。为了消除这种混淆并避免所有编译器警告,我更喜欢(通常)委托而不是姐妹类而不是实现继承。
class ICar
public:
virtual void drive() = 0;
;
class IFastCar : virtual public ICar
public:
virtual void driveFast() = 0;
;
class ICarImpl
public:
void drive_impl() ;
;
class IFastCarImpl
public :
void driveFast_impl() ;
;
class Car : public virtual ICar, protected ICarImpl
public:
void drive() drive_impl();
;
class FastCar : public IFastCar, protected ICarImpl, protected IFastCarImpl
public:
void driveFast() driveFast_impl();
void drive() drive_impl();
;
int main()
FastCar fc;
fc.drive();
fc.driveFast();
ICar* fc_a = new FastCar;
fc_a->drive();
return 0;
这实现了实现继承的通常目标——只保留一组可以在多个具体类之间共享的通用代码,从而使维护更容易。
【讨论】:
也许我应该在这里的姐妹类使用private
继承,而不是protected
...【参考方案4】:
由于 ICar 和 IFastCar 始终是汽车,因此您可以拥有从 ICar 派生的 IFastCar,然后像您一样实现 Car 和 FastCar。
class ICar
public:
virtual void drive() = 0;
;
class IFastCar : ICar
public:
virtual void drive() = 0;
virtual void driveFast() = 0;
;
class Car : ICar
public:
void drive()
cout << "driving a normal car" << endl;
;
class FastCar : IFastCar
public:
void drive()
cout << "driving a fast car the normal way" << endl;
void driveFast()
cout << "driving a fast car at top speed" << endl;
;
int main()
FastCar fc;
fc.drive();
fc.driveFast();
return 0;
【讨论】:
问题是关于避免必须实现FastCar::drive
,这可以通过类似于您在IFastCar
中从ICar
提供继承的方法来解决,必须是虚拟的,您将需要从IFastCar
中删除virtual void drive() = 0;
(然后你可以删除FastCar::drive()
的实现)以上是关于如何让派生类使用基实现来满足接口的主要内容,如果未能解决你的问题,请参考以下文章
C++ - 允许通过基类(接口)访问,禁止通过派生类访问(具体实现)?