我想知道" 虚拟基类 "是什么以及它意味着什么.
让我举个例子:
class Foo { public: void DoSomething() { /* ... */ } }; class Bar : public virtual Foo { public: void DoSpecific() { /* ... */ } };
OJ... 521
虚拟继承中使用的虚拟基类是一种在使用多重继承时防止出现在继承层次结构中的给定类的多个"实例"的方法.
请考虑以下情形:
class A { public: void Foo() {} }; class B : public A {}; class C : public A {}; class D : public B, public C {};
上面的类层次结构导致"可怕的钻石",如下所示:
A / \ B C \ / D
D的实例将由B组成,其中包括A,而C也包括A.所以你有两个"实例"(为了更好的表达)A.
当您有这种情况时,您可能会有歧义.执行此操作时会发生什么:
D d; d.Foo(); // is this B's Foo() or C's Foo() ??
虚拟继承可以解决这个问题.当您在继承类时指定虚拟时,您告诉编译器您只需要一个实例.
class A { public: void Foo() {} }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};
这意味着层次结构中只包含一个A"实例".于是
D d; d.Foo(); // no longer ambiguous
希望有助于作为迷你总结.有关更多信息,请阅读本文和此内容.这里也有一个很好的例子.
虚拟继承中使用的虚拟基类是一种在使用多重继承时防止出现在继承层次结构中的给定类的多个"实例"的方法.
请考虑以下情形:
class A { public: void Foo() {} }; class B : public A {}; class C : public A {}; class D : public B, public C {};
上面的类层次结构导致"可怕的钻石",如下所示:
A / \ B C \ / D
D的实例将由B组成,其中包括A,而C也包括A.所以你有两个"实例"(为了更好的表达)A.
当您有这种情况时,您可能会有歧义.执行此操作时会发生什么:
D d; d.Foo(); // is this B's Foo() or C's Foo() ??
虚拟继承可以解决这个问题.当您在继承类时指定虚拟时,您告诉编译器您只需要一个实例.
class A { public: void Foo() {} }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};
这意味着层次结构中只包含一个A"实例".于是
D d; d.Foo(); // no longer ambiguous
希望有助于作为迷你总结.有关更多信息,请阅读本文和此内容.这里也有一个很好的例子.
作为旁注,Dreaded Diamond的问题在于基类存在多次.因此,通过常规继承,您相信您拥有:
A / \ B C \ / D
但是在内存布局中,你有:
A A | | B C \ / D
这解释了为什么在通话时D::foo()
,你有一个模棱两可的问题.但是当你想要使用一个成员变量时,真正的问题出现了A
.例如,假设我们有:
class A { public : foo() ; int m_iValue ; } ;
当你试图访问m_iValue
时D
,编译器会抗议,因为在层次结构中,它会看到两个m_iValue
,而不是一个.如果你修改一个,比如说,B::m_iValue
(也就是其中的A::m_iValue
父代B
),C::m_iValue
将不会被修改(即它的A::m_iValue
父代C
).
这就是虚拟继承的便利之处,就像它一样,你将回到真正的钻石布局,不仅有一种foo()
方法,而且只有一种方法m_iValue
.
想像:
A
有一些基本功能.
B
添加了一些很酷的数据(例如)
C
添加一些很酷的功能,如观察者模式(例如,打开m_iValue
).
D
继承自B
和C
,从而继承自A
.
与正常的继承,修改m_iValue
由D
是模糊的,这必须得到解决.即使它是,m_iValues
内部有两个D
,所以你最好记住并同时更新这两个.
随着虚拟继承,修改m_iValue
的D
是好的......但是......比方说,你有D
.通过它的C
界面,你附上了一个观察者.通过它的B
界面,你可以更新酷阵列,它具有直接改变的副作用m_iValue
......
由于变化m_iValue
是直接完成(不使用虚拟存取方法),观察者"听",通过C
将不会被调用,因为实施监听的代码是C
,和B
不知道它...
如果您的层次结构中有钻石,则表示您有95%的人对所述层次结构做错了.
使用虚拟基础解释多重继承需要了解C++对象模型.并且清楚地解释该主题最好在文章中而不是在评论框中完成.
我发现最好的,可读的解释解决了我对这个主题的所有怀疑是这篇文章:http://www.phpcompiler.org/articles/virtualinheritance.html
在阅读完之后,您真的不需要阅读有关该主题的任何其他内容(除非您是编译器编写者)
虚基类是无法实例化的类:您无法从中创建直接对象.
我认为你混淆了两件截然不同的事情.虚拟继承与抽象类不同.虚拟继承修改函数调用的行为; 有时它会解析函数调用,否则这些函数调用将是模糊的,有时它会将函数调用处理推迟到非虚拟继承中所期望的类.
我想补充OJ的善意澄清.
虚拟继承不是没有代价的.就像所有虚拟事物一样,你会受到性能影响.这种性能打击有一种方法可能不那么优雅.
而不是通过虚拟导出来破坏钻石,你可以在钻石上添加另一层,得到这样的东西:
B / \ D11 D12 | | D21 D22 \ / DD
这些类都没有虚拟继承,都是公开继承的.然后,类D21和D22将隐藏虚拟函数f(),这对于DD来说是不明确的,可能通过将该函数声明为私有.它们分别定义了一个包装函数f1()和f2(),每个函数调用class-local(private)f(),从而解决冲突.DD类如果需要D11 :: f()则调用f1(),如果需要D12 :: f()则调用f2().如果你内联定义包装器,你可能会得到零开销.
当然,如果您可以更改D11和D12,那么您可以在这些类中执行相同的操作,但通常情况并非如此.