我从未使用多重继承,偶然发现了一个我从未遇到的设计问题。
class A {
//..methods..
}
class B : public A {
int b;
//...methods..
}
class C : public A {
int c1,c2;
}
class D : public B,public C {
}
这是经典钻石。事实是C
实际上只是一个A
,有两个额外的整数。而D
实际上只是B
和C
的聚合,但我觉得多重继承不是为了制作这样的东西。或者可能还有其他最佳做法。
我试图实现多重继承的原因是我想写一个像void func(A*)
这样的函数,并将它传递给A
或D
类指针。我的天真尝试是做一个简单的演员:
void func(A* a) { // <-- I call this with a A or D pointer
// ..do something with A members..
if(is_the_case) { // <-- Im sure the passed "a" pointer is actually a *D
D* d = (D*)a;
// ..do something with the extra 2 ints provided by the C class..
}
}
不起作用..编译得很好,但是在执行if(is_the_case)
时我有一个非常奇怪的行为,清除了2个额外的整数c1
和c2
,也清除{{1 }(由b
继承)。
我对钻石问题进行了重新考虑,但是层次结构中只有一个B
(和2 B
),所以我不明白为什么A
被清除了。只是为了尝试,我在b
和B
声明中使用了公共虚拟。现在每个演员都是编译错误,除非我使用C
..
有人能说清楚幕后发生了什么吗?考虑到其他类如:
,最佳做法是什么dynamic_cast
也就是说,其他类只是派生自class E : public A {
int e;
//..methods..
}
class F : public E,public C {
}
的类的聚合+由A
继承的两个额外的int,并且可以传递给需要C
<的函数/ p>
谢谢,尽我所能尽可能清楚......
答案 0 :(得分:4)
你的代码是有效的,因为你使用的是C风格的强制转换,它可以是reinterpret_cast
,我的理解是你可以reinterpret_cast
在任意两种指针类型之间,即使它没有合理。从多重继承的基础转换为更多派生类时,必须使用dynamic_cast
。 static_cast
会产生编译时错误。事实上,dynamic_cast
同时完成了这两项工作。
void func(A* a) { // <-- I call this with a A or D pointer
// ..do something with A members..
if(D* d = dynamic_cast<D*>(a)) { // a definitely points to a D
// and we got a guaranteed good pointer too
}
}
这是为什么应该避免使用C风格的演员表的一个很好的例子。
答案 1 :(得分:1)
您可以阅读多重继承here。
您需要做的是实际上继承下一个类:
class B : virtual public A {
int b;
//...methods..
};
class C : virtual public A {
int c1,c2;
};
答案 2 :(得分:1)
我不知道为什么你会看到这种行为。如注释中所述,您描述的代码不应该编译。
是否存在设计问题?是。不要基于查询对象的类型明确地做出选择。这就是虚拟功能的用途。
class A
{
public:
virtual void prepare_for_list_insertion() {}
};
class B : public A
{
int b;
};
class C : public A
{
private:
int c1, c2;
protected:
void clear() { c1 = c2 = 0; }
};
class D : public B, public C
{
public:
void prepare_for_list_insertion()
{
clear();
}
};
void func(A* a)
{
a->prepare_for_list_insertion();
}
int main()
{
A a;
func(&a); // calls A::prepare_for_list_insertion
D d;
// You need a cast to disambiguate the A base - either will do.
func(static_cast<C*>(&d)); // calls D::prepare_for_list_insertion
func(static_cast<B*>(&d)); // calls D::prepare_for_list_insertion
}