`

虚函数的继承

 
阅读更多
C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼。虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内存空间中又是如何布局的,却可以对C++的了解深入不少。这段时间花了一些时间了解这些玩意,搞得偶都,不过总算有些收获,嘿嘿。
  
  先看一段代码
  class A
  {
   virtual aa(){};
  };
  
  class B : public virtual A
  {
   char j[3]; //加入一个变量是为了看清楚class中的vfptr放在什么位置
   public:
   virtual bb(){};
  };
  class C : public virtual B
  {
   char i[3];
   public:
   virtual cc(){};
  };
  
  这次先不给结果,先分析一下,也好加强一下印象。
  1、对于class A,由于只有一个虚函数,那么必须得有一个对应的虚函数表,来记录对应的函数入口地址。同时在class A的内存空间中之需要有个vfptr_A指向该表。sizeof(A)也很容易确定,为4。
  2、对于class B,由于class B虚基础了class A,同时还拥有自己的虚函数。那么class B中首先拥有一个vfptr_B,指向自己的虚函数表。还有char j[3],做一次alignment,一般大小为4。可虚继承该如何实现咧?this is 啊 problem!偶之前是不晓得的,还好C++ Object Model上有介绍。首先要通过加入一个虚l类指针(记vbptr_B_A)来指向其父类,然后还要包含父类的所有内容。有些复杂了,不过还不难想象。sizeof(B)= 4+4+4+4=16(vfptr_B、char j[3]做alignment、vbptr_B_A和class A)。
  3、在接着是class C了。class C首先也得有个vfptr_C,然后是char i[3],然后是vbptr_C_B,然后是class B,所以sizeof(C)=4+4+4+16=28(vfptr_C、char i[3]做alignment、vbptr_C_A和class B)。
  
  在VC 6.0下写了个程序,把上面几个类的大小打印出来,果然结果为4、16、28。hoho搞定!真的搞定了?也许经过上面的分析,虽然每个类具体的内存布局还不大清楚,但其中的内容应该不会错了。嘿嘿,在没跟踪时偶确实也是这么想的,但结果却是……
  
  VC中虚继承的内存布局——单继承
  画了个图,简单表示一下我跟踪后的结果
  

  class A的情况太简单,没问题。从class B的内存布局图可以得出下面的结论。
  1、vf_ptr B放在了类的首部,那么如果要想直接拿memcpy完成类的复制是很危险的,用struct也是不行的。改天再深入学习一下struct 和class的区别,可以看出这里的差别来。
  2、vbtbl_ptr_B,为什么不是先前我描述的vbptr_B_A呢?因为这个指针与我先前猜测的内容有很大区别。这个指针指向的是class B的虚类表(嗯,俺自个儿起的名字,实在是学艺不精)。看看VB table,VB table有两项,第一项为FFFFFFFC,这一项的值可能没啥意义,可能是为了保证虚类表不为空吧。第二项为8,看起来像是class B中的class A相对该vbtbl_ptr_B的位移,也就是一个offset。类似的方法在C++ Object Model(P121)有介绍,可以去看看。
  class C的内存布局就比较复杂了,不过它的内存布局也更一步说明我对vbtbl_ptr_B中的内容,也就是虚类表的理解是正确的。不过值得关注的是class B中的class A在布局时被移到前面去了,虽然整个大小没变,但这样一来如果做这样的操作 C c; B *b;b=&c;时b的操作如何呢?此时只要从c的虚类表里获得class B的位置既可赋值给b。但是在构建class C时会复杂一些,后面的使用还是非常简单的,效率也比较高。class A的内存布局被前移可能是考虑倒C的虚继承顺序吧。
  
分享到:
评论

相关推荐

    C++ 多态 虚函数 虚函数表 最是详细

    高质量的C++多态讲解,详细讲解虚函数,虚函数表,虚函数继承,虚函数继承下的内存分配等

    C++虚函数实现原理

    - 当一个类继承自另一个含有虚函数的基类时,派生类会拥有自己的虚函数表,并且该表通常会包含基类虚函数表中的函数地址,以及派生类自身定义的虚函数地址。 2. **虚表指针的初始化:** - 每个对象都会有一个虚表...

    c++虚函数与虚函数表

    `时,`c3`的虚函数指针指向的是`C3`的虚函数表,但由于多重继承的存在,实际上包含了`C1`和`C2`的虚函数地址。 #### 四、虚函数表的实际应用 通过给定的代码示例,我们可以看到虚函数表的实际作用: 1. **通过指针...

    c++继承与多态,虚函数实例

    虚函数在实现多态中扮演着关键角色,它可以确保程序在运行时能够调用到正确的方法,即便对象的实际类型与指针或引用声明的类型不同。 首先,让我们深入理解“虚函数”这一概念。在C++中,虚函数通过在基类中使用`...

    构造函数不能声明为虚函数,析构函数可以声明为虚函数

    在类的继承体系中,基类的析构函数可以声明为虚函数,这样做的主要原因如下: 1. **避免内存泄漏**:如果基类指针指向派生类对象,并且基类的析构函数不是虚函数,那么通过基类指针删除派生类对象时,只会调用基类...

    继承,虚函数,抽象类

    在编程世界中,继承、虚函数和抽象类是面向对象编程(OOP)中的核心概念。这三者共同构建了类的层次结构,使得代码更加模块化,易于维护和扩展。接下来,我们将深入探讨这三个概念及其相关用法。 首先,**继承**是...

    C++虚函数表解析

    3. 如果类有继承关系,子类的虚函数表会包含父类的虚函数表,并在其后添加新的、重写的或新增的虚函数指针。 在C++中,我们可以利用`dynamic_cast`等类型转换操作符,配合虚函数表来实现运行时类型检查。例如,当...

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

    总结来说,C++的虚函数和虚继承是实现多态性和解决多重继承问题的关键机制,它们带来了一些额外的内存开销和复杂性,但为面向对象编程提供了灵活性和强大的功能。理解和掌握这些概念对于深入理解C++至关重要。

    C++虚函数表测试源码

    2. 派生类定义:继承自基类,并可能重写基类的虚函数,如: ```cpp class Derived : public Base { public: void func1() override { /*...*/ } void func2() { /*...*/ } void func3() { /*...*/ } // 非虚函数 ...

    C++ 虚函数表详解

    C++虚函数表详解 C++中的虚函数表是实现多态机制的关键组件。...4. 继承中的虚函数表:在继承中,虚函数表的实现机制也很重要,用于解决继承和覆盖的问题,无论是有覆盖还是无覆盖,虚函数表都起到了关键的作用。

    c++虚函数表解析(彻底攻克继承和虚函数)

    彻底搞清楚继承是个什么东西 彻底搞清楚虚函数和虚函数表是个什么东西

    虚函数表工作原理

    虚函数表是C++实现多态性的重要机制,主要针对含有虚函数的类。多态允许我们使用父类指针调用子类重写的成员函数,实现动态绑定或运行时绑定。虚函数表(Virtual Table,简称V-Table)就是一个存放类中所有虚函数...

    虚函数.ppt

    虚函数的概念源于类的继承层次结构。当一个基类指针或引用指向其派生类的对象时,如果我们想通过这个基类指针调用派生类重写的方法,就必须使用虚函数。在C++中,我们通过在基类函数声明前加上`virtual`关键字来标记...

    指针高级应用_虚函数

    在继承层次结构中,当基类指针或引用指向派生类对象时,通过虚函数调用,我们可以根据实际的对象类型来执行相应的函数实现。虚函数的关键在于虚函数表(vtable),这是一个由编译器自动创建的动态查找表,其中包含了...

    c++多态和虚函数表

    动态多态的核心在于继承和虚函数表(virtual table)的使用。 ##### 2.1 继承与虚函数 继承是面向对象编程的一个基本特性,通过继承,子类可以继承父类的属性和方法。在C++中,如果希望一个类支持动态多态,则需要在...

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

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

Global site tag (gtag.js) - Google Analytics