• 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 CB : public virtual 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的vbtable的首地址(vbptr)到CA的元素之间的偏移量。此时,不会生成各子类的函数f标识,除非子类重写了该函数,从而达到“共享”的目的(这里的具体内存布局,可以参看钻石型继承内存布局,在白杨的那篇文章中也有)(VS2010中,在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutX,可以查看类X的对象布局)。
    也正因此,此时的sizeof(CD) = 12(vbptrCB + vbptrCC + sizoef(int))(32位机中指针占4个字节);
    另注:
    如果CB,CC中各定义一个int型变量,则sizeof(CD)就变成20(两个vbptr + 3个sizoef(int)
    如果CA中添加一个virtual void f1(){},sizeof(CD) = 16(vfptrCA +vbptrCB + vbptrCC + sizoef(int));
    再添加virtual void f2(){},sizeof(CD) = 16不变。原因如下所示:带有虚函数(大于等于1个)的类,其内存布局上包含一个指向虚函数列表的指针(vfptr)(虚函数表指针),这跟该类有几个虚函数无关。
     
     
    引用:
    1. 虚继承_百度百科  http://baike.baidu.com/link?url=Q-BGdH5Cs5CtDRti1VaDPOI5ws1pVK4INmwGXp4NYrUWQ4xSMdgTu5CAmEBg9YMeA-Npeg3QiOthoIpSmVmlyK#2_1
     
    实例:
    1. 多继承的困惑:
    #include <iostream.h>
    #include <memory.h>
    
    #if 1
    #define VIRTUAL virtual
    #else
    #define VIRTUAL 
    #endif
    
    class CA
    {
        int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性
    public:
        VIRTUAL void f() {cout << "CA::f" << endl;} //不论基类的方法是否是虚函数,子类继承后都会有他的实例。
    };
    class CB : public CA
    {
    };
    class CC : public CA
    {
    };
    class CD : public CB, public CC
    {
    };
    
    int main(void)
    {
        CD d;
        d.f();
        return 0;
    }
    /*
    test.cpp:29: error: request for member ‘f’ is ambiguous
    */
  • 相关阅读:
    寒假的Java学习笔记总结1
    寒假的Java学习笔记总结0
    归档七
    数组归档
    课后作业感悟2
    作业感悟1
    《大道至简》第二章(是懒人创造了方法)读后感
    postman接口测试-文件上传
    postman接口测试:获取所有信息
    怎么测接口?
  • 原文地址:https://www.cnblogs.com/mylinux/p/4725833.html
Copyright © 2020-2023  润新知