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

时间:2011-10-12 15:48:58

标签: c++ inheritance virtual-inheritance

我有以下两个接口,它们不是继承层次结构的一部分。然后我有两个具体的类,一个来自另一个。

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由两个不同的团队创建,分为两个不同的项目。团队已就共享的操作的共同方法签名达成一致。我在实现类中使用了继承来尝试重用相同的实现部分。

4 个答案:

答案 0 :(得分:5)

问题在于这里发生了多重继承......即使FastCar来自Car,基础中的drive()版本也是如此-class Car仅覆盖ICar::drive(),而不是IFastCar::drive()。因此,由于您从FastCar派生IFastCar,因此您需要在FastCar中定义一个函数,该函数也会覆盖纯虚拟抽象方法IFastCar::drive()。 ..继承的Car::drive()不会自动覆盖IFastCare::drive(),因为它不会从该类继承。 IFastCar::drive()ICar::drive()是两个不同的纯抽象虚函数,需要单独重写。如果您想让IFastCar::drive()的界面调用您继承的Car::drive()函数,那么您将需要委托该继承的函数,因为您已经在FastCar::drive()的{​​{1}}版本中完成了称其基类为'版本drive()

答案 1 :(得分:3)

问题在于Car未实现IFastCar::drive,而只实现ICar::drive。第一个设计问题是为什么IFastCar不扩展ICar接口并重新定义相同的操作?

如果由于某种原因不是一个选项,那么你可以做的最简单的事情就是在IFastCar::drive()中通过将请求转发到FastCar方法来实现Car::drive

void FastCar::drive() {
   Car::drive();
}

答案 2 :(得分:2)

你在这里使用virtual遗产是非常有意义的,并且是一个红色的鲱鱼。 virtual继承仅用于从公共基类派生一次; 仅为匹配的成员签名引入一个声明。

所以,你的FastCar具体类实际上有3个成员,而不是2个成员:

virtual void ICar::drive()
virtual void IFastCar::drive()
virtual void IFastCar::driveFast()

两个不同的drive()似乎是相关的,因此您的设计似乎存在缺陷。您不希望virtual继承 - 您可能希望IFastCarICar派生。

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将采用的一些基本功能,那么您可以从FastCar派生Car >这就是为什么你想要virtual继承。请记住在钻石顶部正下方的位置应用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;
}

这实现了实现继承的通常目标 - 只保留一组可在多个具体类之间共享的通用代码,使维护更容易。

答案 3 :(得分:0)

由于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;
}