我想知道什么是“虚拟基类”以及它的含义。
让我举个例子:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
我想知道什么是“虚拟基类”以及它的含义。
让我举个例子:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
当前回答
关于内存布局
作为旁注,可怕的钻石的问题是基类出现了多次。通过常规遗传,你相信你有:
A
/ \
B C
\ /
D
但是在内存布局中,你有:
A A
| |
B C
\ /
D
这解释了为什么当调用D::foo()时,你有一个歧义问题。但真正的问题是当你想要使用a的成员变量时。例如,假设我们有:
class A
{
public :
foo() ;
int m_iValue ;
} ;
当你试图从D访问m_iValue时,编译器会抗议,因为在层次结构中,它会看到两个m_iValue,而不是一个。如果你修改了一个,比如B::m_iValue (B的A::m_iValue父元素),C::m_iValue将不会被修改(C的A::m_iValue父元素)。
这就是虚拟继承方便的地方,有了它,你会回到一个真正的菱形布局,不仅只有一个foo()方法,还有一个且只有一个m_iValue。
会出什么问题呢?
想象一下:
A有一些基本特征。 B添加了一些很酷的数据数组(例如) C给它添加了一些很酷的特性,比如观察者模式(例如,在m_iValue上)。 D继承了B和C,因此也继承了A。
对于普通继承,从D修改m_iValue是不明确的,必须解决这个问题。即使它是,在D中有两个m_iValues,所以你最好记住这一点,并同时更新这两个。
使用虚拟继承,从D修改m_iValue是可以的…但是…假设你有d,通过它的C接口,你连接了一个观察者。通过它的B接口,你更新了酷数组,这有直接改变m_iValue的副作用…
由于m_iValue的改变是直接完成的(不使用虚拟访问方法),通过C“监听”的观察者将不会被调用,因为实现监听的代码是在C中,而B不知道它…
结论
如果你的层次结构中有一颗钻石,这意味着你有95%的概率在该层次结构中做了错事。
其他回答
这意味着对虚函数的调用将被转发到“正确的”类。
C+ FAQ上升。
简而言之,它通常用于多继承场景,其中形成了“菱形”层次结构。虚继承将打破在底层类中创建的模糊性,当您调用该类中的函数并且函数需要解析到底层类之上的类D1或D2时。有关图表和详细信息,请参阅FAQ项。
它还用于姐妹委托,这是一个强大的功能(尽管不适合心脏薄弱的人)。请看这个FAQ。
参见Effective c++第3版中的第40项(第2版中的43项)。
关于内存布局
作为旁注,可怕的钻石的问题是基类出现了多次。通过常规遗传,你相信你有:
A
/ \
B C
\ /
D
但是在内存布局中,你有:
A A
| |
B C
\ /
D
这解释了为什么当调用D::foo()时,你有一个歧义问题。但真正的问题是当你想要使用a的成员变量时。例如,假设我们有:
class A
{
public :
foo() ;
int m_iValue ;
} ;
当你试图从D访问m_iValue时,编译器会抗议,因为在层次结构中,它会看到两个m_iValue,而不是一个。如果你修改了一个,比如B::m_iValue (B的A::m_iValue父元素),C::m_iValue将不会被修改(C的A::m_iValue父元素)。
这就是虚拟继承方便的地方,有了它,你会回到一个真正的菱形布局,不仅只有一个foo()方法,还有一个且只有一个m_iValue。
会出什么问题呢?
想象一下:
A有一些基本特征。 B添加了一些很酷的数据数组(例如) C给它添加了一些很酷的特性,比如观察者模式(例如,在m_iValue上)。 D继承了B和C,因此也继承了A。
对于普通继承,从D修改m_iValue是不明确的,必须解决这个问题。即使它是,在D中有两个m_iValues,所以你最好记住这一点,并同时更新这两个。
使用虚拟继承,从D修改m_iValue是可以的…但是…假设你有d,通过它的C接口,你连接了一个观察者。通过它的B接口,你更新了酷数组,这有直接改变m_iValue的副作用…
由于m_iValue的改变是直接完成的(不使用虚拟访问方法),通过C“监听”的观察者将不会被调用,因为实现监听的代码是在C中,而B不知道它…
结论
如果你的层次结构中有一颗钻石,这意味着你有95%的概率在该层次结构中做了错事。
虚拟基类用于虚拟继承,是一种使用多重继承时防止在继承层次结构中出现给定类的多个“实例”的方法。
考虑以下场景:
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和C组成,B包含A, C也包含A。所以你有两个A的“实例”(为了更好的表达)。
在这种情况下,就有可能出现模棱两可的情况。当你这样做时会发生什么:
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
虚拟继承可以解决这个问题。当您在继承类时指定virtual时,您是在告诉编译器您只需要一个实例。
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
这是一个小总结。要了解更多信息,请阅读这个和这个。这里还有一个很好的例子。
虚基类是这样的类 不能被实例化:你不能 从它创建直接对象。
我认为你混淆了两件完全不同的事情。虚拟继承与抽象类不是一回事。虚继承修改函数调用的行为;有时它会解析否则会有歧义的函数调用,有时它将函数调用处理推迟到非虚继承中期望的类。
你有点让人困惑了。我不知道你是否混淆了一些概念。
op中没有虚基类,只有基类。
你做了虚拟继承。这通常用于多重继承,以便多个派生类使用基类的成员而不复制它们。
不实例化具有纯虚函数的基类。这需要Paul掌握的语法。通常使用它是为了派生类必须定义这些函数。
我不想再解释了,因为我不太明白你在问什么。