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;
其中p
和q
是IControllable*
,但p
指向一个
MachineControllabe
,但 q
到 LightControllable
。作为一个
一般规则(有个例外),你应该制定
接口不可复制和不可分配。此外,通常,他们
还将包含纯虚函数,这意味着您
无法实例化它们。在任一情况下,
std::vector<IControllable>
甚至无法编译。
您可能想要的是std::vector<IControllable*>
。
无论如何,多态性只能通过指针或引用起作用
(而且你不能有引用向量)。
【讨论】:
你能举个例子“他应该能够通过界面中的虚函数使用对象”@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++ 继承、接口的主要内容,如果未能解决你的问题,请参考以下文章