`
- 浏览:
723969 次
- 性别:
- 来自:
大连
-
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的虚继承顺序吧。
分享到:
Global site tag (gtag.js) - Google Analytics
相关推荐
高质量的C++多态讲解,详细讲解虚函数,虚函数表,虚函数继承,虚函数继承下的内存分配等
- 当一个类继承自另一个含有虚函数的基类时,派生类会拥有自己的虚函数表,并且该表通常会包含基类虚函数表中的函数地址,以及派生类自身定义的虚函数地址。 2. **虚表指针的初始化:** - 每个对象都会有一个虚表...
`时,`c3`的虚函数指针指向的是`C3`的虚函数表,但由于多重继承的存在,实际上包含了`C1`和`C2`的虚函数地址。 #### 四、虚函数表的实际应用 通过给定的代码示例,我们可以看到虚函数表的实际作用: 1. **通过指针...
虚函数在实现多态中扮演着关键角色,它可以确保程序在运行时能够调用到正确的方法,即便对象的实际类型与指针或引用声明的类型不同。 首先,让我们深入理解“虚函数”这一概念。在C++中,虚函数通过在基类中使用`...
多重继承下的虚函数表在C++中扮演着关键角色,特别是在实现多态性时。多态允许使用基类指针调用子类的重写成员函数,使得代码更加灵活和可扩展。虚函数表(VFTable)是实现这一功能的核心机制。 虚函数表是一个存放...
在类的继承体系中,基类的析构函数可以声明为虚函数,这样做的主要原因如下: 1. **避免内存泄漏**:如果基类指针指向派生类对象,并且基类的析构函数不是虚函数,那么通过基类指针删除派生类对象时,只会调用基类...
C++ 虚函数使用 C++ 虚函数是实现多态机制的主要手段。多态机制允许使用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这是一种泛型技术,旨在使用不变的代码来实现可变的算法。 ...
在编程世界中,继承、虚函数和抽象类是面向对象编程(OOP)中的核心概念。这三者共同构建了类的层次结构,使得代码更加模块化,易于维护和扩展。接下来,我们将深入探讨这三个概念及其相关用法。 首先,**继承**是...
##### 一般继承(无虚函数覆盖) 假设存在以下继承关系: ```cpp class Derived : public Base { public: // 没有重写基类的虚函数 }; ``` 在这种情况下,`Derived` 类的对象的虚函数表与 `Base` 类的虚函数表...
总结来说,C++的虚函数和虚继承是实现多态性和解决多重继承问题的关键机制,它们带来了一些额外的内存开销和复杂性,但为面向对象编程提供了灵活性和强大的功能。理解和掌握这些概念对于深入理解C++至关重要。
2. 派生类定义:继承自基类,并可能重写基类的虚函数,如: ```cpp class Derived : public Base { public: void func1() override { /*...*/ } void func2() { /*...*/ } void func3() { /*...*/ } // 非虚函数 ...
C++虚函数表详解 C++中的虚函数表是实现多态机制的关键组件。...4. 继承中的虚函数表:在继承中,虚函数表的实现机制也很重要,用于解决继承和覆盖的问题,无论是有覆盖还是无覆盖,虚函数表都起到了关键的作用。
彻底搞清楚继承是个什么东西 彻底搞清楚虚函数和虚函数表是个什么东西
虚函数表是C++实现多态性的重要机制,主要针对含有虚函数的类。多态允许我们使用父类指针调用子类重写的成员函数,实现动态绑定或运行时绑定。虚函数表(Virtual Table,简称V-Table)就是一个存放类中所有虚函数...
虚函数的概念源于类的继承层次结构。当一个基类指针或引用指向其派生类的对象时,如果我们想通过这个基类指针调用派生类重写的方法,就必须使用虚函数。在C++中,我们通过在基类函数声明前加上`virtual`关键字来标记...
在继承层次结构中,当基类指针或引用指向派生类对象时,通过虚函数调用,我们可以根据实际的对象类型来执行相应的函数实现。虚函数的关键在于虚函数表(vtable),这是一个由编译器自动创建的动态查找表,其中包含了...
动态多态的核心在于继承和虚函数表(virtual table)的使用。 ##### 2.1 继承与虚函数 继承是面向对象编程的一个基本特性,通过继承,子类可以继承父类的属性和方法。在C++中,如果希望一个类支持动态多态,则需要在...
### C++继承、虚函数与多态性专题 #### 继承的概念与访问权限 **继承** 是面向对象编程中的一个核心概念,它允许我们创建一个新的类(派生类),该类继承另一个类(基类)的属性和方法。继承提高了代码的复用性和...
### 虚函数和虚继承及其在内存中布局 #### 一、虚函数的理解与内存布局 虚函数是C++中实现多态的一种机制。它允许派生类重定义基类中的虚函数,并且能够在运行时根据对象的实际类型来调用相应的函数版本。 **1.1 ...
如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。 使用虚函数时,必须通过指向基类的指针或基类对象的引用来调用虚函数,其格式为:`指向基类的指针变量名->虚函数名(实参表)`或`基类对象的引用名....