1

我有以下一组课程:

在此处输入图像描述

以及以下代码:

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

现在鉴于有这样的函数F::foo(),为了调用它,我必须转换aE*or D*

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

据我所知,为了调用而进行强制a转换是非法的,因为它是 type ; 对我来说,在调用之前检查条件,听起来像是一个设计问题。有人可以就如何改进这个类层次结构提供一些建议吗?F*F::fooaA*foo

ps 使用此工具绘制图表。

4

4 回答 4

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不是 的实例F,那么 dynamic_cast 返回 null
  • 如果您不使用虚拟继承,那么您最终可能会得到模棱两可的基类。Adynamic_cast到一个不明确的基础将失败(返回 null)。在这个例子中,没有模棱两可的基础,但你必须意识到这一点。
  • 我在大多数类上都省略了虚拟析构函数,但这只是因为我很懒。

如果您反复发现自己处理的对象都是两者的实例,A那么F如果可能的话,这应该反映在类层次结构中。例如,您可以定义一个从和G虚拟继承的类型。然后and可以继承自而不是,并且您可以将 a 传递给该代码,该代码期望它可以调用。AFDEGFG*A*Foo()

于 2013-03-06T09:09:21.753 回答
2

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

除此之外,您可能会反映接口是否F扩展了A接口,或者它是否完全不相关。如果它是一个扩展,那么F应该可能来自A; 在创建一个已知实现扩展接口的对象时,将其地址分配给F*,并避免所有未来的强制转换。(一般来说,不要分配给 anA*直到你到达一些指向的对象将不会实现的点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,因此通常采用以下规则更简单:从接口派生是虚拟的。)

如果F真的与 无关A,那么您甚至可能会问一个类在做什么来实现两者。A或者,如果某些(许多?)实现也实现是有意义的F,您可以考虑提供F对以下接口的访问A:比如虚函数F* A::getF() { return NULL; };也实现的类F将使用类似F* E::getF() { return this; }.

于 2013-03-06T09:34:15.503 回答
2

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

看看你的模型的正式组织,我会说你可以添加一个虚函数A,这两者D都会E覆盖。然后,这些覆盖会将实现委托给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(); /* ... */ }
    // ...
};
于 2013-03-06T09:00:48.790 回答
1

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

如果F 只是实现细节,另一种方法是将要处理的对象列表保留为Fs,并将要处理的对象保留为As。同样,正如安迪所说,这将取决于您的情况的语义。

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();
}
于 2013-03-06T09:25:29.650 回答