对象内存布局 (12)

下面来看看虚基类对对象内存布局的影响。虚基类的主要作用就是在所有的派生类中,保留且仅保留一份虚基类的suboject。

 

a. 一个虚基类的情况

#include <iostream>
using namespace std;

class Base
{
public:
    int base_member;
};
class Derived : public virtual Base {};

int main(void)
{
    Base b;
    Derived d;
    cout << sizeof(b) << endl;
    cout << sizeof(d) << endl;
    return 0;
}

运行结果:

注意使用了虚继承,即class Derived : public virtual Base。这次Derived的对象大小为什么为8 bytes呢?这是因为编译器会给Derived对象安插一个虚基类表指针vbptr,下面给出Derived对象的memory layout:

 

 

虚基类表指针vbptr指向Derived类的virtual bass class table(虚基类表),虚基类表中存放的是Derived类的虚基类表指针到虚基类实例指针的偏移量

 在main函数的return前,增加如下语句:

#include <iostream>
using namespace std;

class Base
{
public:
    int base_member;
};

class Derived : public virtual Base {};

int main(void)
{
    Base b;
    Derived d;
    cout << sizeof(b) << endl;
    cout << sizeof(d) << endl;
    cout << "Derived object d's vbptr = " << (unsigned long*)(&d) << endl;

    cout << "Address of virtual base class table = " << (unsigned long*)*(unsigned long*)(&d) << endl;

    cout << "Item 1 in virtual base class table = " << *(unsigned long*)*(unsigned long*)(&d) << endl;

    cout << "Item 2 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 1) << endl;

    cout << "Item 3 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 2) << endl;

    cout << "The address of virtual base class Base = " << (Base*)(&d) << endl;
    return 0;
}

编译后运行结果:

编译后运行结果:

不难发现,虚基类示例地址 = vbptr + offset,即0x0012FF78 = 0x0012FF74 + 4。图示如下:

b. 我们来看看Derived有两个虚基类的情况:

#include <iostream>
using namespace std;

class Base1
{
public:
    int base1_member;
};
class Base2
{
public:

    int base2_member;
};

class Derived : public virtual Base1, public virtual Base2 {};

int main(void)
{
    Base1 b1;

    Base2 b2;

    Derived d;

    cout << sizeof(b1) << endl;

    cout << sizeof(b2) << endl;

    cout << sizeof(d) << endl;

    cout << "Derived object d's vbptr = " << (unsigned long*)(&d) << endl;

    cout << "Address of virtual base class table = " << (unsigned long*)*(unsigned long*)(&d) << endl;

    cout << "Item 1 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 0) << endl;

    cout << "Item 2 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 1) << endl;

    cout << "Item 3 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 2) << endl;

    cout << "Item 3 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 3) << endl;

    cout << "The address of virtual base class: Base1's instance = " << (Base1*)(&d) << endl;

    cout << "The address of virtual base class: Base2's instance = " << (Base2*)(&d) << endl;

    return 0;

}

编译运行结果如下:

 

Derived对象的memory layout图解如下:

 

 

不管Derived类有多少个虚基类,它只有一个vbptr和一个virtual base class table

时间: 2024-10-25 01:31:38

对象内存布局 (12)的相关文章

对象内存布局 (14)

前言 07年12月,我写了一篇<C++虚函数表解析>的文章,引起了大家的兴趣.有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的.我在这里一并对大家的留言表示感谢.这也是我为什么再写一篇续言的原因.因为,在上一篇文章中,我用了的示例都是非常简单的,主要是为了说明一些机理上的问题,也是为了图一些表达上方便和简单.不想,这篇文章成为了打开C++对象模型内存布局的一个引子,引发了大家对C++对象的更深层次的讨论.当然,我之前的文章还有很多方面没有涉及,从我个人感觉下来,在谈论虚函数

对象内存布局 (11)

注意:关于内存对齐(memory alignment),请看关于内存对齐问题,后面将会用到.   下面我们进行在普通继承(即非虚继承)时,派生类的指针转换到基类指针的情形研究.假定各类之间的关系如下图:   代码如下: #include <iostream> using namespace std; class Parent { public: int parent; }; class Child : public Parent { public: int child; }; class Gr

对象内存布局 (7)

在对象内存布局 (5)的代码中,在Derived类中覆盖Base1中声明的vfBase1_1(),其他代码不变.修改后的代码如下: #include <iostream> using namespace std; class Base1 { public: int m_base1; inline virtual void vfBase1_1() { cout << "This is in Base1::vfBase1_1()" << endl; }

对象内存布局 (6)

如果在对象内存布局 (5)的代码中,将Base1中的两个虚函数声明删除,同时将main函数中的下面代码注释掉(因为现在只有两张虚函数表了): 代码如下: #include <iostream> using namespace std; class Base1 { public: int m_base1; /*inline virtual void vfBase1_1() { cout << "This is in Base1::vfBase1_1()" <

对象内存布局 (10)

在对象内存布局 (9)基础上做些修改:派生类override基类的虚函数,即Base2 override Base1中声明的虚函数vfBase1(),Base3 override Base1中声明的虚函数vfBase1()和Base2中声明的虚函数vfBase2(), Derived override Base1中声明的虚函数vfBase1().Base2中声明的虚函数vfBase2()和Base3中声明的虚函数vfBase3().修改如下: #include <iostream> using

对象内存布局 (13)——上一篇的纠正

下面来看看虚基类对对象内存布局的影响.虚基类的主要作用就是在所有的派生类中,保留且仅保留一份虚基类的suboject. #include <iostream> using namespace std; class Base { public: int m_base; Base():m_base(20){} virtual void vfBase_1() { cout << "This is in Base::vfBase_1()" << endl;

对象内存布局 (15)

重复继承   下面我们再来看看,发生重复继承的情况.所谓重复继承,也就是某个基类被间接地重复继承了多次.   下图是一个继承图,我们重载了父类的f()函数.   其类继承的源代码如下所示.其中,每个类都有两个变量,一个是整形(4字节),一个是字符(1字节),而且还有自己的虚函数,自己overwrite父类的虚函数.如子类D中,f()覆盖了超类的函数, f1()和f2() 覆盖了其父类的虚函数,Df()为自己的虚函数. class B {     public:         int ib;  

vs2008下C++对象内存布局(3):加上虚函数

这次我们为父类加上虚函数: class CParent { public: int parent_a; int parent_b; public: virtual void parent_f1() { parent_a = 0x10; } virtual void parent_f2() { parent_b = 0x20; } }; class CChild : public CParent { public: int child_a; int child_b; public: virtual

对象内存布局 (1)

内容概要: 满足下面2个条件时, 1. 父类有虚函数,子类无虚函数(即无虚函数重写或无虚函数覆盖) 2. 非虚继承 类对象之内存布局   前述相关内容参考: 1. http://blog.csdn.net/pathuang68/archive/2009/04/20/4096088.aspx 2. http://blog.csdn.net/pathuang68/archive/2009/04/21/4096429.aspx 3. http://blog.csdn.net/pathuang68/ar