Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48212
  • 博文数量: 12
  • 博客积分: 291
  • 博客等级: 二等列兵
  • 技术积分: 147
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-15 14:25
文章分类

全部博文(12)

文章存档

2011年(12)

我的朋友

分类: C/C++

2011-09-04 10:31:00

    C++中的虚函数的内部实现机制到底是怎样的呢?
    鉴于涉及到的内容有点多,我将分三篇文章来介绍。
    第一篇:对象内存模型浅析,这里我将对对象的内存模型进行简单的实验和总结。
    第二篇:继承对象的构造和析构浅析,这里我将对存在继承关系的对象的构造和析构进行简单的实验和总结。
    第三篇:虚函数的内部机制浅析,这里我将对虚函数内部的实现机制进行实验总结。
    我使用的编译器是VS2008,有不足或者不准确的地方,欢大家拍砖(我个人非常迫切的希望得到大家的指正),我会及时修正相关内容。

    开始正题:对象内存模型浅析:
    
  1. #include <tchar.h>
  2. #include <iostream>
  3. using namespace std;

  4. #pragma pack (1)

  5. class Person
  6. {
  7. private:
  8.     int m_nAge;
  9. };

  10. class Man : public Person
  11. {
  12. private:
  13.     double m_dHeight;
  14. };

  15. int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
  16. {
  17.     Person Jack;
  18.     Man Mike;
  19.     cout << sizeof(Jack) << endl;
  20.     cout << sizeof(Mike) << endl;
  21.     return 1;
  22. }
    首先解释一下#pragma pack(1)这条语句的作用,它要求编译器将字节对齐的最小单位设定为1个字节。
    关于字节对齐,简单的解释就是,假定一个32位的CPU,读取一个存储在内存中的int型的变量,如果该int变量存放在内存中的首地址是偶地址,那么CPU一个周期就能读出这32bit的数据,如果该int变量存放在内存中的首地址是奇地址,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。所以,如果我们将字节对齐设置为4个字节,那么理论上,CPU执行我们代码的速度要比将字节对齐设置为1个字节的速度要快。
    所以,如果我们将字节对齐设置为8个字节,那么
    int nNum1;
    double dNum2;
将会占用16个字节的大小,而如果我们将字节对齐设置为1个字节,那么它将会占用12个字节的大小,上述代码将字节对齐设置为1个字节,是为了防止字节对齐干扰了我们对于对象内存模型的实验。
    回到主题,上述代码的执行结果如下:
    4
    12
    我们看到,Person类对象占用了4个字节大小的内存空间,Man类对象占用了12个字节的大小的内存空间,所以,Man类中实际上有两个成员变量,int m_nAge和double m_dHeight,所以可以得出一个结论:派生类对象中同时包含基类的成员变量。
    那么它们在内存中的位置是怎样的呢?
  1. #include <tchar.h>
  2. #include <iostream>
  3. using namespace std;

  4. #pragma pack (1)

  5. class Person
  6. {
  7. private:
  8.     int m_nAge;
  9. };

  10. class Man : public Person
  11. {
  12. private:
  13.     double m_dHeight;
  14. };

  15. class Woman : public Person
  16. {
  17. private:
  18.     double m_dWigth;
  19. };

  20. int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
  21. {
  22.     Person Jack;
  23.     Man Mike;
  24.     Woman Susan;
  25.     cout << &Jack << endl;
  26.     cout << &Mike << endl;
  27.     cout << &Susan << endl;
  28.     return 1;
  29. }
上述代码输出了Person类对象和Man类对象的地址,执行结果如下:
0012FF60
0012FF4C
0012FF38
我们知道,0012FF60和0012FF4C之间有14个字节的内存空间,0012FF38和0012FF4C之间有14个字节的内存空间,我们将Man类对象分成Person基类部分(int m_nAge)和Man派生类部分(double m_dHeight),将Woman类对象分成Person基类部分(int m_nAge)和Woman派生类部分(double m_dWeight)。那么,Man类和Woman类的对象内存模型如下:


所以,
  1. Person Jack;
  2. Man Mike;
  3. Woman Susan;
这三行代码实际上产生了3个Person基类部分、一个Man派生类部分和一个Woman派生类部分,而非像代码中写的表意那样,有一个Person基类部分,一个Man派生类部分和一个Woman派生类部分。
类的继承和派生只是简化方便我们程序员编写代码,并不会简化派生类对象占用的内存大小。
阅读(717) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~