我有以下几组课程:
以下代码:
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绘制图表。
答案 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)。在这个例子中没有模棱两可的基础,但你必须要注意它。如果您反复发现自己处理的是A
和F
实例的对象,则应尽可能在类层次结构中反映出来。例如,您可以定义几乎从G
和A
继承的类型F
。然后D
和E
可以继承G
而不是F
,您可以将G*
传递给期望A*
开启的代码它可以调用Foo()
。
答案 1 :(得分:2)
很难在不知道类的确切语义的情况下给出设计建议(字母只是符号,因此必须假设这些继承关系是正确的,而它们可能不是)。 / p>
只要查看模型的正式组织,我就会说你可以向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(); /* ... */ }
// ...
};
答案 2 :(得分:2)
在不知道不同类的角色的情况下,很难
比如说,但如果A
和F
是接口(可能是这种情况),那么
给定A*
,正确的方式询问对象是否也
支持接口F
为dynamic_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
的接口,通常更简单
采用规则:从界面推导是虚拟的。)
如果F
与A
真正无关,那么您甚至可能会问什么
一个班级正在实施两个。或者如果它有意义的话
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();
}