如何让派生类使用基实现来满足接口

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,基类Cardrive()的版本只覆盖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++ - 允许通过基类(接口)访问,禁止通过派生类访问(具体实现)?

C++:如何强制派生类设置基成员变量?

是否可以通过引用以基类作为参数的函数来传递派生类

给定一个基类作为参数,如果传递了派生类,我如何使用 op<< 重载来打印派生类的特征?

Typescript派生类和抽象类

C#编程,关于基类和派生类