关于虚拟继承的思考

转载说明:http://blog.csdn.net/billdavid/article/details/24317


虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要(因为这样只会降低效率和占用更多的空间,实在是一无是处)。
以下面的一个例子为例:
#include <iostream.h>
#include <memory.h>

class
 CA
{

    int
 k; //为了便于说明后面的内存结构特别添加
public:
    void
 f() {cout << "CA::f" << endl;}
};


class
 CB :  public CA
{
};


class
 CC :  public CA
{
};


class
 CD : public CB, public CC
{
};


void
 main()
{

    CD d;
    d.f();
}

当编译上述代码时,我们会收到如下的错误提示:
error C2385: 'CD::f' is ambiguous
即编译器无法确定你在d.f()中要调用的函数f到底是哪一个。这里可能会让人觉得有些奇怪,命名只定义了一个CA::f,既然大家都派生自CA,那自然就是调用的CA::f,为什么还无法确定呢?
这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CB、CC时还需要在编译器的语法树中生成CB::f,CC::f等标识,那么,在编译CD的时候,由于CB、CC都有一个函数f,此时,编译器将试图生成两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译)

要解决这个问题,有两个方法:
1
、重载函数f():此时由于我们明确定义了CD::f,编译器检查到CD::f()调用时就无需再像上面一样去逐级生成CD::f标识了;
此时CD的元素结构如下:
--------
|
CB(CA)|
|
CC(CA)|
--------

故此时的sizeof(CD) = 8;(CB、CC各有一个元素k)
2
、使用虚拟继承:虚拟继承又称作共享继承,这种共享其实也是编译期间实现的,当使用虚拟继承时,上面的程序将变成下面的形式:
#include <iostream.h>
#include <memory.h>

class
 CA
{

    int
 k;
public
:
    void
 f() {cout << "CA::f" << endl;}
};


class
 CB :  virtual public CA
{
};


class
 CC :  virtual public CA
{
};


class
 CD : public CB, public CC
{
};


void
 main()
{

    CD d;
    d.f();
}

此时,当编译器确定d.f()调用的具体含义时,将生成如下的CD结构:
----
|
CB|
|
CC|
|
CA|
----

同时,在CB、CC中都分别包含了一个指向CA的vbptr(virtual base table pointer),其中记录的是从CB、CC的元素到CA的元素之间的偏移量。此时,不会生成各子类的函数f标识,除非子类重载了该函数,从而达到“共享”的目的。
也正因此,此时的sizeof(CD) = 12(两个vbptr + sizoef(int);
所有这一切都是编译期间决定的,只是编译器为了提供这样一个新的语法功能为我们多作了一些事情而已。

注:以上讨论限MS Visual C++编译器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值