`
lc52520
  • 浏览: 369194 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

虚继承

阅读更多
虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在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));
所有这一切都是编译期间决定的,只是编译器为了提供这样一个新的语法功能为我们多作了一些事情而已。
另注:

  • 如果CB,CC中各定义一个int型变量,则sizeof(CD)就变成20(两个vbptr + 3个sizoef(int)


  • 如果CA中添加一个virtual void f1(){},sizeof(CD) = 16(两个vbptr + sizoef(int)+vptr);
  • 再添加virtual void f2(){},sizeof(CD) = 16不变。原因如下所示:带有虚函数的类,其内存布局上包含一个指向虚函数列表的指针(vptr),这跟有几个虚函数无关。

注:以上讨论限MS Visual C++编译器。
分享到:
评论

相关推荐

    面向对象C++用虚继承来消灭菱形结构二义性,解决俩爷爷的问题.zip

    通过阅读《C++通过虚继承来消灭菱形结构消除二义性》、《C++因为多继承而产生了菱形结构》、《C++虚继承的意义和使用》、《C++中虚继承的作用及底层实现原理》以及《C++对象模型:单继承,多继承,虚继承》等相关...

    C++ 虚继承对基类构造函数调用顺序的影响

    继承作为面向对象编程的一种基本特征,其使用频率...  假设derived 虚继承自base类,那么derivd与base是一种“has a”的关系,即derived类有一个指向base类的vptr。(貌似有些牵强!某些编译器确实如此)  因此虚

    详解虚继承

    ### 详解虚继承 #### 一、引言 在C++编程语言中,虚继承(Virtual Inheritance)是一项重要的特性,它主要用于解决多重继承时基类成员被多次继承的问题,尤其是当这些基类中包含非静态成员变量时。本文将从语法、...

    多重继承和虚继承.doc

    多重继承和虚继承是C++中两种高级的继承方式,它们允许类从多个基类中获取属性和行为,但同时也引入了一些复杂性和潜在的问题。 **多重继承** 是指一个类可以从多个基类派生,从而获得多个基类的特性。在多重继承中...

    8.虚继承的布局1

    虚继承是一种C++中的继承方式,它允许一个基类被多个派生类共享,从而避免了多继承导致的“菱形问题”(即一个对象有多重同级的基类,而这些基类又共享了一个共同的基类)。虚继承的主要目的是解决基类的多重实例化...

    虚函数和虚继承及其在内存中布局

    ### 虚函数和虚继承及其在内存中布局 #### 一、虚函数的理解与内存布局 虚函数是C++中实现多态的一种机制。它允许派生类重定义基类中的虚函数,并且能够在运行时根据对象的实际类型来调用相应的函数版本。 **1.1 ...

    C++课件 类、流、虚继承

    本课件将深入探讨类的访问控制、继承与派生,以及虚继承等关键知识点。 首先,我们来讨论类成员的访问控制。C++提供了三种访问修饰符:public、private和protected。public成员对所有代码都是可见的,可以在任何...

    虚继承的代码说明(附注释)

    虚继承的代码说明(附注释)

    9.虚继承带未覆盖函数的对象布局1

    虚继承是一种面向对象编程中的高级特性,用于解决多重继承中可能出现的“菱形问题”,即多个派生类各自继承自同一个基类,可能导致一个对象在内存中有多个基类的副本。这种情况下,虚继承可以确保所有派生类共享同一...

    虚继承.cpp

    虚继承.cpp

    虚继承的代码说明

    关于虚继承的代码说明

    C++ 内存布局虚继承 ---Empty virtual base classs (空虚基类)

    C++ 内存布局虚继承 ---Empty virtual base class (空虚基类) C++ 中的虚拟继承是一种特殊的继承机制,它允许一个派生类从多个基类继承,而不产生钻石继承问题。虚拟继承的实现方式是在编译器的实现中,并且不同的...

    c++基础语法:虚继承

    虚继承是C++中一种解决多继承问题的机制,它主要用来处理多个子类共同继承同一个父类时可能出现的重复数据问题。在没有虚继承的情况下,每个子类都会包含一份父类的副本,这可能导致内存浪费和二义性。下面我们将...

    探索虚继承的本质.cpp

    探索虚继承的本质.cpp

    C++中虚函数工作原理和(虚)继承类的内存占用大小计算1

    然而,虚继承会导致一些额外的复杂性和内存开销,因为每个虚继承的派生类都会有一个额外的虚指针来指向共享的基类部分的虚函数表。 内存占用方面,每个含有虚函数的类通常会增加4个字节(对于大多数32位系统)的...

    关于C++中菱形继承和虚继承的问题总结

    C++菱形继承和虚继承的问题总结 C++是一种高效、灵活的编程语言,它提供了封装、继承和多态等三大特性。继承是C++中的一种重要机制,它允许一个类继承另一个类的成员和方法,使得程序设计更加灵活和高效。但是在...

    C++多重继承与虚继承分析

    本文以实例形式较为全面的讲述了C++的多重继承与虚继承,是大家深入学习C++面向对象程序设计所必须要掌握的知识点,具体内容如下: 一、多重继承 我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类...

Global site tag (gtag.js) - Google Analytics