虚函数在对象中的内存布局

典型地,C++通过虚函数实现多态性。多态性的定义:无论发送消息的对象属于什么类,他们均发送具有相同形式的消息,对消息的处理方式可能随接受消息的对象而变。具体地说,在某个基类上建立起来的类的层次结构中,可以对任何一个派生类的对象中的同名成员函数进行调用,而被调用的成员变量所提供的处理可以随其所属的类而改变。虚函数首先是一种成员函数,它可以在该类的派生类中被重新定义并被赋予另外一种处理功能。

 

我们先来看看下面的例子:

#include <iostream>

using namespace std;

 

class A

{

private:

         int a;

public:

         void funA0()

         {

                   cout << "This is funA0 in class A" << endl;

         }

 

         void setA(int a)

         {

                   this->a = a;

         }

};

 

class B

{

private:

         int b;

public:

         virtual void vfunB0()

         {

                   cout << "This is vfunB0 in class B" << endl;

         }

 

         void setB(int b)

         {

                   this->b = b;

         }

};

 

class C

{

private:

         int c;

public:

         virtual void vfunC0()

         {

                   cout << "It's in vfunC0 in class C" << endl;

         }

 

         virtual void vfunC1()

         {

                  cout << "It's in vfunC1 in class C" << endl;

         }

 

         void setC(int c)

         {

                   this->c = c;

         }

};

 

class D : public B

{

private:

         int d0;

         int d1;

public:

         void setD0(int d0)

         {

                   this->d0 = d0;

         }

 

         void setD1(int d1)

         {

                   this->d1 = d1;

         }

};

 

int main(void)

{

         A aa;

         B bb;

         C cc;

         D dd;

 

         cout << "Size of class A = ";

         cout << sizeof(aa) << endl;

 

         cout << "Size of class B = ";

         cout << sizeof(bb) << endl;

       

         cout << "Size of class C = ";

         cout << sizeof(cc) << endl;

 

         cout << "Size of class D = ";

         cout << sizeof(dd) << endl;

 

         dd.setD0(10);

         dd.setD1(11);

 

         cout << *((unsigned long*)(&dd) + 1) << endl;

         cout << *((unsigned long*)(&dd) + 2) << endl;

         cout << *((unsigned long*)(&dd) + 3) << endl;

 

         return 0;

}

输出结果:

虚函数在对象中的内存布局 - 玄机逸士 - 玄机逸士博客

1

class A中定义了一个整形的成员变量a,在32-bit的操作系统中,整形是4Bytes,因此aa的大小是4Bytes

虚函数在对象中的内存布局 - 玄机逸士 - 玄机逸士博客

2

class B中定义了一个整形的成员变量b和一个虚函数vfunB0,其中整形成员变量b需要4Bytesvfun0将导致class B生成一个指向virtual table的指针vfptr (其中virtual table中拥有该类所有虚函数的指针)。在32-bit的操作系统中一个指针需要32 bits或者4Bytes来表示,因此输出的bb的大小是8Bytes

虚函数在对象中的内存布局 - 玄机逸士 - 玄机逸士博客 

3

class C中定义了一个整形的成员变量c和两个虚函数vfunC0vfunC1,整形成员变量c需要4Bytes,两个虚函数的声明导致class C生成一个指向virtual table的指针vfptr (其中virtual table中拥有该类所有虚函数的指针)。在32-bit的操作系统中一个指针需要32 bits或者4Bytes来表示,因此输出的cc的大小也是是8Bytes

虚函数在对象中的内存布局 - 玄机逸士 - 玄机逸士博客

4

由此可见,只要在一个类中声明了一个虚函数或者多个虚函数,该类在实例化的时候,总是隐式地生成一个且仅一个指向virtual table的指针vfptr,一个或者多个该类虚函数的指针将被存放在virtual table中。另外,如果一个类存在虚函数,那么vfptr总是在最前面的,即在所有成员变量之前,如上图所示。

 

class D中定义了一个整形的成员变量d0d1,整形成员变量d0d1各需要4Bytes,另外由于它继承了class B,由于在class B中也定义了一个整形成员变量b (尽管它是private),这个也需要4Bytes,同时在class B 也定义的一个虚函数,也被class D继承了,而虚函数不管被继承多少次,仍然是虚函数,因此class D也需要在被实例化的时候隐式生成一个指向virtual table的指针vfptr。由此得到dd的大小是16Bytes

虚函数在对象中的内存布局 - 玄机逸士 - 玄机逸士博客

5

为什么说是这样的顺序呢?即bd0d1,而不是d0d1b呢,从下面的代码所产生的结果就可以很清楚地知道这一点。

         dd.setD0(10);             // 设置d0 = 10

         dd.setD1(11);             // 设置d1 = 11

 

         // *((unsigned long*)(&dd) + 0) 就是Virtual Table的指针

         //

         cout << *((unsigned long*)(&dd) + 1) << endl;                    // 参照前面console的输出:3435973836

         cout << *((unsigned long*)(&dd) + 2) << endl;                    // 参照前面console的输出:10

         cout << *((unsigned long*)(&dd) + 3) << endl;                    // 参照前面console的输出:11

因此可以判定最后两个分别是d0d1,紧随vfptr之后的是class B中的似有成员变量b,由于没有被赋值,所以输出的结果是任给的。

 

由此我们可以得到一个重要的结论:尽管是私有成员变量,诸如class D中的d0d1,我们还是可以通过类似

*((unsigned long*)(&dd) + offset)的方式轻松地访问到,甚至其基类的私有成员变量,诸如class B中的b。(这样似乎不太安全,但事实上就是这样的,当然很少有人这么做)

 

同一个类的所有对象共享同一个成员函数的地址空间(虚函数除外),而每个对象有独立的成员变量地址空间。可以说成员函数是类拥有的,成员变量是类的对象拥有的。

 

虚函数和虚继承问题相当复杂,尤其是两者同时存在的时候。但并不是很难,也非常有意思。



FROM: http://patmusing.blog.163.com/blog/static/13583496020103255219855/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值