假设我们有通常的钻石图案:
class A
{
public:
virtual char foo() = 0;
virtual ~A() {}
};
class B : public A
{
public:
virtual char foo() { return 'B';}
virtual ~B() {}
};
class C : public A
{
public:
virtual char foo() { return 'C';}
virtual ~C() {}
};
class D : public B, public C
{
public:
virtual char foo() { return 'D';}
virtual ~D() {}
};
请注意,基类A
没有任何数据成员。它实际上只是一个纯虚方法的接口。
如果我这样做:
D* d = new D();
A* a = static_cast<A*>(d);
编译器会告诉我,由于有两个A基类,所以转换是不明确的。
但是,如果我这样做:
D* d = new D();
B* b = static_cast<B*>(d); // Or C* c = static_cast<C*>(d);
A* a = static_cast<A*>(b);
现在我有一个指向我A
基类之一的指针,我可以a->foo()
。
这样安全吗?
我想知道的是,如果我可以进行双向上转换,只需要指向接口(没有数据成员),而不需要虚拟继承的开销。我不打算向下转向指针,也不打算用虚拟方法调用它。
我知道这意味着有两个基类,但由于没有成员,它应该无关紧要,或者是这样吗?
EDIT
:
我正在努力寻找实现接口的方法,我想我只需要使用虚拟继承。
假设我有一个类Buffer
(接口类),以及一个派生自它的BufferImplementation
类。
现在假设我有另一个接口IOBuffer
(来自其他接口Buffer
),其中IOBufferImplementation
类必须从BufferImplementation
和{{IOBuffer
派生1}} interface。
在我之前的例子中,Buffer是A,BufferImplementation是B,IOBuffer是C,IOBufferImplementation是D。
答案 0 :(得分:1)
Is this safe?
我是这么认为的。您正在使用两个路径之一从派生类D
到基准A
。您现在可以安全地使用接口成员。
如果A
中的纯虚拟方法在overridden
中不是D
,那么使用D->B->A
路径只需一条注释始终在overridden
中调用B
方法(类似于D->C->A
路径),尽管C
中的实现是有意/有用的。当A
拥有数据成员时,这也会有效,在此特定设置中,将使用通过D->B->A
继承的数据成员。
答案 1 :(得分:0)
你所写的不是钻石图案!
如果你想要钻石图案,你必须virtual inherit
!
class B : virtual public A
和
class C : virtual public A
您的版本只是从基类派生线性,这使得D包含基类A的两倍。因此,如果您要求,编译器无法知道应该选择哪个A.如果您使用虚拟导出,则D中只能获得一个A.
请记住,您必须手动调用D中的A的构造函数。如果C或D的构造函数调用A的构造函数!如果您创建D!
的实例,则不会发生此调用