C++:派生类和基类实现单个接口?

12

在C++中,是否可以让基类和派生类实现一个单一接口呢?

例如:

class Interface
{
    public:
        virtual void BaseFunction() = 0;
        virtual void DerivedFunction() = 0;
};

class Base
{
    public:
        virtual void BaseFunction(){}
};

class Derived : public Base, public Interface
{
    public: 
        void DerivedFunction(){}
};

void main()
{
    Derived derived;
}

这个失败是因为Derived无法实例化。就编译器而言,Interface::BaseFunction从未被定义。

到目前为止,我找到的唯一解决方案是在Derived中声明一个传递函数。

class Derived : public Base, public Interface
{
    public: 
        void DerivedFunction(){}
        void BaseFunction(){ Base::BaseFunction(); }
};

有没有更好的解决方案?


编辑:如果有影响,这里是我在使用MFC对话框时遇到的实际问题。

我有一个对话框类(假设是MyDialog),它派生自CDialog。由于依赖性问题,我需要创建一个抽象接口(MyDialogInterface)。使用MyDialogInterface的类需要使用特定于MyDialog的方法,但还需要调用CDialog :: SetParent。我刚刚通过创建MyDialog :: SetParent并使其传递到CDialog :: SetParent来解决了这个问题,但想知道是否有更好的方法。

5个回答

17

C++没有意识到从基类继承的函数已经实现了BaseFunction:在从Interface派生的类中必须显式实现该函数。更改如下:

class Interface
{
    public:
        virtual void BaseFunction() = 0;
        virtual void DerivedFunction() = 0;
};

class Base : public Interface
{
    public:
        virtual void BaseFunction(){}
};

class Derived : public Base
{
    public: 
        virtual void DerivedFunction(){}
};

int main()
{
    Derived derived;
}

如果您希望只实现其中一个接口,可以将Interface拆分为两个接口:

class DerivedInterface
{
    public:
        virtual void DerivedFunction() = 0;
};

class BaseInterface
{
    public:
        virtual void BaseFunction() = 0;
};

class Base : public BaseInterface
{
    public:
        virtual void BaseFunction(){}
};

class Derived : public DerivedInterface
{
    public: 
        virtual void DerivedFunction(){}
};  

class Both : public DerivedInterface, public Base {
    public: 
        virtual void DerivedFunction(){}
};

int main()
{
    Derived derived;
    Base base;
    Both both;
}

注意:main函数必须返回int类型。
注意:在派生类中,即使不是强制要求,也最好在继承自基类的虚函数前添加virtual关键字。


2个问题:
  • 现在无法单独实例化Base。
  • 其他从Base派生的类现在必须实现DerivedFunction。 class OtherDerived : public Base { public: void OtherDerivedFunction(){} };
- Joe
在您的原始示例中,无论如何都无法实例化Base。第二个问题也是您原始示例的问题。 - Torlack
我喜欢你的回答,但我认为透传函数实际上是最简单的。你的Both类定义了自己的实现,而我想要一种使用Base和Derived中的实现的方法。我意识到我可能正在像傻瓜一样设计我的类。 :) - Joe
对于第二个位,我认为你的意思是“class Derived : public Base, public DerivedInterface”。 - Joe
@JohannesSchaub-litb: "保持虚拟是一个好的实践": 现在我们有C++11定义的override,应该更新这个。 - jpo38
显示剩余8条评论

4

看起来Derived“是一个”Base并不完全正确,这表明包含可能比继承更好。

此外,您的Derived成员函数也应该声明为虚拟的。

class Contained
{
    public:
        void containedFunction() {}
};

class Derived
{
    public:
        virtual void derivedFunction() {}
        virtual void containedFunction() {return contained.containedFunction();}
    private:
        Containted contained;
};

如果您想隐藏实现细节,可以将包含的成员设置为引用或智能指针。


1
问题在于你的示例中有两个Interface的实现,一个来自Base,另一个来自Derived。这是C++语言的设计。正如已经指出的那样,只需在Derived的定义中删除Interface基类即可。

嗯,不对...再检查一下代码。Base没有继承Interface。(Interface有一个未在Base中实现的DerviceFunction) - James Curran

1

我同意litb的答案。然而,在这里有一个机会去理解虚函数和多重继承是如何工作的。

当一个类有多个基类时,它会为每个基类拥有单独的虚函数表。 Derived 将拥有一个如下所示的虚函数表结构:

Derived
 vtable: Interface
   BaseFunction*
   DerivedFunction*
 vtable: Base
   BaseFunction*

此外,每个基类只能看到自己的虚函数表。当实例化“Base”时,它会填充虚函数表中的“Base::BaseFunction”指针,但无法看到“Interface”的虚函数表。
如果您提供的代码可以编译,则“Derived”的实例的虚函数表结构如下:
Derived
 vtable: Interface
   BaseFunction* = 0
   DerivedFunction* = Derived::DerivedFunction
 vtable: Base
   BaseFunction* = Base::BaseFunction

0

我发现litb的回答中有一件事情是不足的。如果我有一个Derived实例,我可以得到一个DerivedInterface和一个BaseInterface。但是如果我只有一个DerivedInterface,我无法得到一个BaseInterface,因为从BaseInterface派生DerivedInterface是行不通的。

但是,这整个时间我一直限制自己只进行编译时检查,出于某种原因。这个DerivedInterface非常好用:

class DerivedInterface
{
    public:
        virtual void DerivedFunction() = 0;
        BaseInterface* GetBaseInterface()
            {return dynamic_cast<BaseInterface*>(this);}
};

void main()
{
    Derived derived;

    DerivedInterface* derivedInterface = &derived;
    derivedInterface->GetBaseInterface()->BaseFunction();
}

在派生类中不需要传递函数,每个人都很开心。当然,它不再严格是一个接口,但没关系。为什么我之前没有想到呢?:)


这没有问题。你只需要创建一个从DerivedInterface和BaseInterface派生的类,然后你就拥有了与你原来问题中的“Interface”类完全相同的接口。 - Johannes Schaub - litb
1
无论如何,永远不要像那样使用dynamic_cast :) dynamic_cast应该是最后的手段,用来解决通过设计正确接口无法完成的问题。在您的情况下,只需按照我在上面评论中所说的做Interface,然后您就可以执行BaseInterface* baseInterface = &derived;并调用两者 :) - Johannes Schaub - litb
是的,那会给我相同的接口,但不同的实现方式,这就是重点。我有一个基类和派生类的实现,需要一个单一的接口来使用它们。听起来这已经是最接近的解决方案了。 - Joe
等一下,我是在指你关于“both”类的那个,它定义了自己的实现。嗯嗯。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接