C++ 继承、接口

Posted

技术标签:

【中文标题】C++ 继承、接口【英文标题】:C++ inheritance, interfaces 【发布时间】:2015-06-07 16:26:37 【问题描述】:

假设我有一个接口IControllable,3 个类继承了该接口:MachineControllable, LightControllable, OtherControllable,其中有一些特定的数据和方法。

现在我只想拥有所有IControllable 中的一个容器,所以我创建了一个矢量容器。

vector<IControllable> allControllables; // and put all the MachineControllable,
//LightControllable, OtherControllable here by the IControllable interface class.

但是现在的问题是,我只能使用IControllable定义的东西,而不是具体可控的具体数据和方法。

我是否应该为每个 Controllable 使用单独的容器,或者我的逻辑在 OOP 方面有什么错误?

【问题讨论】:

【参考方案1】:

“我是否应该为每个 Controllable 设置单独的容器,或者我的逻辑在 OOP 方面有什么错误?”

不,你的逻辑没问题。问题是,你不能实例化一个抽象类。

您应该有一个容器来保存指向IControllable 接口的指针,例如:

 vector<IControllable*> allControllables;

 vector<std::unique_ptr<IControllable>> allControllables;

 vector<std::shared_ptr<IControllable>> allControllables;

【讨论】:

可能不是unique_ptr;一个容器应该拥有这样的对象是非常罕见的。可能是shared_ptr,但大多数时候,原始指针是最合适的。 是的,对 unique_ptr 也有同样的想法,但答案一扫而光,谢谢。 另外一点:他的代码编译的事实让我认为他的IControllable 不是抽象的(这可能是他的设计错误——接口类通常应该是抽象的)。 @JamesKanze 我没有看到任何证据,OP 说,代码可以编译。 很难确定,因为英语显然不是OP的母语,但是像“我只能使用IControllable定义的东西”这样的陈述似乎表明他可以使用一些东西;即代码编译。【参考方案2】:

如前所述,您的向量应该包含某种类型的指针,而不是基础对象本身。

一旦你有了它,你需要一种方法来获取指向容器中实际对象类型的特定指针。标准方式是dynamic_cast:

IMachineControllable * p = dynamic_cast<IMachineControllable*>(allControllables[i]);

如果你选择了错误的类型,你会得到一个 NULL 指针。

如果这些是 Microsoft COM 接口,则需要使用另一种方法,但除非你声明这是你所拥有的,否则我不会深入讨论。

【讨论】:

正在考虑这个问题,并且对演员表有相同的想法,谢谢你的例子。我对 c++/oop 的知识已经很模糊了,只是有时很难把它们放在一起^^ 我会非常怀疑需要dynamic_cast 才能工作的界面。有些情况是合适的,但很少见。他应该可以通过界面中的虚函数来使用对象。 你能举个例子“他应该能够通过界面中的虚函数使用对象”@JamesKanze 吗?【参考方案3】:

这可能不是唯一的问题。容器需要价值 语义,并包含您放入的对象的副本 他们。继承通常不适用于复制和 作业:想想如果你有这可能意味着什么:

*p = *q;

其中pqIControllable*,但p 指向一个 MachineControllabe,但 qLightControllable。作为一个 一般规则(个例外),你应该制定 接口不可复制和不可分配。此外,通常,他们 还将包含纯虚函数,这意味着您 无法实例化它们。在任一情况下, std::vector&lt;IControllable&gt; 甚至无法编译。

您可能想要的是std::vector&lt;IControllable*&gt;。 无论如何,多态性只能通过指针或引用起作用 (而且你不能有引用向量)。

【讨论】:

你能举个例子“他应该能够通过界面中的虚函数使用对象”@James Kanze 吗? @Tomas 你知道什么是虚函数吗?或者什么是纯虚函数:接口通常包含纯虚函数。 (否则,它不是接口。)通常,在多态层次结构中,所有行为都应该可以通过接口中的纯虚函数获得。 (派生类扩展接口或实现多个接口时会有例外。但您应该从简单开始,情况并非如此。)【参考方案4】:

但是现在的问题是,我只能使用 IControllable 什么的 定义,而不是具体Controllable的具体数据和方法。

我是否应该为每个 Controllable 使用单独的容器,或者我的 在 OOP 方面逻辑是错误的?

这取决于你想对你的对象做什么。

如果您需要通过IControllable 接口大量使用所有IControllable 对象,那么将它们全部放在一个容器中是有意义的。

另一方面,如果您希望大量使用它们的特定接口,那么使用单独的容器也是有意义的。

如果你需要两者都做,那么两者都做没有错。

现在,如果您选择将所有内容放在一个容器中,那么您必须使用某种指针/智能指针,因为存储不同类型按值会导致切片并且不允许多态 em> 执行。

但是,如果可能的话,最好按将对象存储在容器中。因此,如果您使用 多个 容器存储 按值 会更好。

如果你想两者都做,那么你可以将对象按值存储在单独的容器中,并将它们的指针存储在一个包罗万象的容器中。在这种情况下,按值存储的容器将拥有对象,因此包罗万象的容器应该拥有它们 - 为此使用原始指针:

struct IControllable  virtual ~IControllable()  ;
struct MachineControllable: IControllable ;
struct LightControllable: IControllable ;
struct OtherControllable: IControlable ;

// store by value if possible (not always possible)
std::vector<MachineControllable> machingControlables;
std::vector<LightControllable> lightControlables;
std::vector<OtherControlable> otherControlables;

std::vector<IControlable*> allControlables; // raw pointers (non owned)

如果您不想单独存储对象,那么您的包罗万象的容器需要拥有这些对象:

// these objects die when this container dies.
std::vector<std::unique_ptr<IControlable>> allControlables;

所以真正的问题是,您将如何花费大部分时间将这些作为特定类型和/或一般(基本)类型进行处理?

还有你想要你的数据结构有多复杂?如果您使用多个容器,则会增加管理数据的复杂性。

请记住,如果您不为您的 特定 类型使用单独的容器,则必须强制转换它们以进行 特定 调用:

for(auto& controlable: allControlables)

    MachineControllable* mc;
    LightControllable* lc;
    OtherControllable* oc;

    if((mc = dynamic_cast<MachineControllable*>(controlable.get())))
        mc->machine_specific();
    else if((lc = dynamic_cast<LightControllable*>(controlable.get())))
        lc->light_specific();
    else if((oc = dynamic_cast<OtherControllable*>(controlable.get())))
        oc->other_specific();

不理想。

【讨论】:

以上是关于C++ 继承、接口的主要内容,如果未能解决你的问题,请参考以下文章

C++接口继承

带有接口的 C++ 多重继承?

C++中的接口继承

c++ COM接口继承

C++ 中的继承和接口

接口讲解