从基类到不同派生类的多重继承转换

时间:2013-03-06 08:57:25

标签: c++ casting multiple-inheritance

我有以下几组课程:

enter image description here

以下代码:

A* a;
if(condition)
{
    a = new E();
}
else
{
    a = new D();
}

现在假设有F::foo()这样的功能,为了调用它,我必须将a投射到E*D*

if(condition)
{
    ((E*)a)->foo();
}
else
{
    ((D*)a)->foo();
}

据我所知,将a投射到F*以拨打F::foo将是非法的,因为a的类型为A*;对我来说,在调用foo之前检查一个条件,听起来像是一个设计问题。有人可以就如何改进这个类层次结构提出一些建议吗?

P.S。使用this tool绘制图表。

4 个答案:

答案 0 :(得分:5)

#include <iostream>

struct A { virtual ~A() {} };

struct C : virtual A {};

struct B : virtual A {};

struct F {
    virtual void Foo() { std::cout << "ok\n"; }
};

struct E : C, virtual F {};

struct D : B, virtual F {};


int main() {
    A *a = new E();
    dynamic_cast<F*>(a)->Foo();
}
  • 如果你陷入困境并且a的referand不是F的实例,那么dynamic_cast将返回null
  • 如果您不使用虚拟继承,那么最终可能会出现含糊不清的基类。对于模糊基数的dynamic_cast将失败(返回null)。在这个例子中没有模棱两可的基础,但你必须要注意它。
  • 我在大部分课程中都遗漏了虚拟析构函数,但这只是因为我很懒。

如果您反复发现自己处理的是AF实例的对象,则应尽可能在类层次结构中反映出来。例如,您可以定义几乎从GA继承的类型F。然后DE可以继承G而不是F,您可以将G*传递给期望A*开启的代码它可以调用Foo()

答案 1 :(得分:2)

很难在不知道类的确切语义的情况下给出设计建议(字母只是符号,因此必须假设这些继承关系是正确的,而它们可能不是)。 / p>

只要查看模型的正式组织,我就会说你可以向A添加一个虚拟函数,DE都会覆盖它。然后,这些覆盖将实现委托给F::foo()

class A { 
public:
    virtual void bar() { }; // Maybe make this pure if A is abstract
    // ...
};

// ...

class D : public C, public F { 
public:
    virtual void bar() { /* ... */ f::foo(); /* ... */ }
    // ...
};

class E : public B, public F { 
public:
    virtual void bar() { /* ... */ f::foo(); /* ... */ }
    // ...
};

答案 2 :(得分:2)

在不知道不同类的角色的情况下,很难 比如说,但如果AF是接口(可能是这种情况),那么 给定A*,正确的方式询问对象是否也 支持接口Fdynamic_cast<F*>。这给了 你是一个指向F接口的指针,如果支持它,和 否则为空指针。

除此之外,您可能会反映F接口是否扩展 A接口,或者它是否完全不相关。如果它 是一个扩展名,然后F应该来自A;什么时候 创建一个已知实现扩展接口的对象, 您将其地址分配给F*,并避免所有未来的演员表。 (一般情况下,在达到某一点之前,请勿指定A* 指向的某些对象将实现F。) 所以你最终得到的结果是:

//  interfaces...
class A {};
class F : public virtual A {};

//  implementations of A...
class C : public virtual A {};
class B : public virtual A {};

//  implementations of F (and also A, of course)
class E : public C, public virtual F {};
class D : public B, public virtual F {};

请注意,从界面派生时,通常是 一个好主意,使派生虚拟。 (在这种情况下,它 是A的所有派生所必需的。但既然如此 模式可以在另一个层次重复,有一些新类 扩展F的接口,通常更简单 采用规则:从界面推导是虚拟的。)

如果FA真正无关,那么您甚至可能会问什么 一个班级正在实施两个。或者如果它有意义的话 A的一些(很多?)实现也会实现F,你 可能会考虑提供对F的访问权限作为界面的一部分 A:说一个虚函数F* A::getF() { return NULL; }; 同时实现F的类将覆盖此函数 用F* E::getF() { return this; }

之类的东西

答案 3 :(得分:1)

如果F 只是一个实现细节,那么你应该做@AndyProwl所说的。在基类A中创建虚函数。

如果F 不是只是一个实现细节,另一种方法是保留您想要处理的对象列表F和您想要的对象作为A来处理。再次,正如安迪所说,这将取决于你的情况的语义。

vector<F*> effs;
vector<A*> ehs;

A* a;
F* f;
if(condition) {
    E* e = new E();
    a = e;
    f = e;
}
else {
    D* d = new D();
    a = d;
    f = d;
}

effs.push_back(f);
ehs.push_back(a);

for(A* a: ehs) {
    a->bar();
}
for(F* f: effs) {
    f->foo();
}