浅度探索C++对象模型

好吧,我承认标题是用来搞笑的,因为内容实在谈不上有什么深度,只是介绍下内存布局,深度不够只好浅度来凑:-)

单刀直入,这次博客计划讨论如下三种情形下的C++对象内存布局:

1.单继承带覆盖;

2.带重复继承的多重继承带覆盖;

3.带重复继承的多重虚拟继承带覆盖

之所以只给出这三种情况是因为个人认为其余的情形是平凡的,被以上三种所蕴含;其次这几种情况都是包含成员变量的,这也是和上篇博客的区别所在;最后就是这篇决定调整一下叙述方式:先给出预期结果,然后通过运行结果来验证我们的想法。

以下程序运行环境均是32位WIN7系统 + Visual Studio 2010。

一、单继承带覆盖:

先上代码:

 1 #include <iostream>
 2 using namespace std;
 3
 4 //单继承带覆盖有数据成员和成员函数的情况下内存布局
 5 class Base{
 6
 7 public:
 8     Base():_base(10){}
 9     virtual void f(){ cout<<"Base::f()"<<endl; }
10 private:
11     int _base;
12 };
13
14 class Derive: public Base{
15
16 public:
17     Derive():_derive(100){}
18     virtual void f(){ cout<<"Derive::f()"<<endl;}
19     virtual void g(){ cout<<"Derive::g()"<<endl;}
20 private:
21     int _derive;
22 };
23
24 class DDerive: public Derive{
25
26 public:
27     DDerive():_dderive(1000){}
28     virtual void f(){ cout<<"DDerive::f()"<<endl;}
29     virtual void g(){ cout<<"DDerive::g()"<<endl;}
30     virtual void h(){ cout<<"DDerive::h()"<<endl;}
31 private:
32     int _dderive;
33 };
34
35 int main(){
36
37     DDerive d;
38     typedef void (*Fun)();
39     Fun pFun = NULL;
40     //首先通过函数指针调用成员函数
41     pFun = (Fun)*((int*)*((int*)(&d) + 0) + 0);
42     pFun();
43     pFun = (Fun)*((int*)*((int*)(&d) + 0) + 1);
44     pFun();
45     pFun = (Fun)*((int*)*((int*)(&d) + 0) + 2);
46     pFun();
47     //再通过函数指针访问派生类中的数据成员:
48     cout<<"Base::_base = "        <<*(int*)((int*)(&d) + 1)<<endl;
49     cout<<"Derive::_derive = "    <<*(int*)((int*)(&d) + 2)<<endl;
50     cout<<"DDerive::_dderive = "<<*(int*)((int*)(&d) + 3)<<endl;
51
52     getchar();
53     return 0;
54 }

预期内存布局图示如下:

运行结果为:

可以看到,运行结果和预期完全一致。

二、带重复继承的多重继承带覆盖:

三、带重复继承的多重虚拟继承带覆盖:

时间: 2024-11-05 17:27:54

浅度探索C++对象模型的相关文章

深度探索C++对象模型的读书心得

参考:深度探索C++对象模型 (stanley B.Lippman著 侯捷翻译) 1. Page9 : C++对象模型,说明每一个类仅有一个虚函数表Vtbl,而类的每一个对象都有指向其表的指针. 2. Page30:引用也需要与一个指针(大小为4BYTE)相同的空间. 3. Page28: 指针类型会教导编译器如何解释某个特定地址中的内存内容及大小. 4. Page39: explicit关键字能够制止"单一参数的constructor被当做一个Conversion运算符" 5. Pa

【深度探索C++对象模型】第二章 构造函数语意学(上)

第二章 构造函数语意学(The Semantics of Constructors) -- 本书作者:Stanley B.Lippman 一.前言 首先让我们来梳理一个概念: 默认构造函数(Default Constructor) : 是在没有显示提供初始化式时调用的构造函数.它由不带任何参数的构造函数,或是为所有形参提供默认实参的构造函数定义.如果定义的某个类的成员变量没有提供显示的初始化式时,就会调用默认构造函数(Default Contructor). 如果用户的类里面,没有显示的定义任何

[读书系列] 深度探索C++对象模型 初读

2012年底-2014年初这段时间主要用C++做手游开发,时隔3年,重新拿起<深度探索C++对象模型>这本书,感觉生疏了很多,如果按前阵子的生疏度来说,现在不借助Visual Studio之类的工具的话,写C++代码感觉要比较费劲,最近重读了这本书后,感觉要写点东西下来(因为懒得用笔作笔记,太慢了-_-!)加深下印象. 以前还是新手的时候,总是认为: 1.class如果没有定义任何constructor的话,编译器会自动合成一个default constructor(我习惯叫缺省的构造函数)出

《深度探索c++对象模型》chapter1关于对象对象模型

在c++中,有2种class data member:static和nostatic,以及3钟class member function:static,nostatic和virtual.已知下面这个class Point声明: class Point { public: Point(float xval); virtual ~Point(); float x() const; static int PointCount(); protected: virtual ostream& print(o

柔性数组-读《深度探索C++对象模型》有感 (转载)

最近在看<深度探索C++对象模型>,对于Struct的用法中,发现有一些地方值得我们借鉴的地方,特此和大家分享一下,此间内容包含了网上搜集的一些资料,同时感谢提供这些信息的作者. 原文如下: 例如,把单一元素的数组放在一个struct的尾端,于是每个struct objects可以拥有可变大小的数组.    code: struct mumble { //stuff char pc[1];    };        //从档案或标准输入装置中取得一个字符串,然后为struct本身和该字符串配置

【深度探索C++对象模型】第一章 关于对象

第一章 关于对象(Object Lessons) -- 本书作者:Stanley B.Lippman 一.前言 什么是 C++ 对象模型:简单的说,就是 C++ 中面向对象的底层实现机制. 本书组织: 第 1 章,关于对象(Object Lessons),介绍 C++ 对象的基础概念,给读者一个粗略的了解. 第 2 章,构造函数语意学(The Semantics of Constructors),构造函数什么时候会被编译器合成?它给我们的程序效率带来了怎样的影响? 第 3 章,Data语意学(T

深度探索C++对象模型第6章 执行期语意学

(一)对象的构造和析构(Object Construction and Destruction) 一般而言我们会把object尽可能放置在使用它的那个程序区段附近,这么做可以节省非必要的对象产生操作和摧毁操作. 全局对象 如果我们有以下程序片段: Matrix identity main() { //identity 必须在此处被初始化 Matrix m1=identity; ... return 0; } C++保证,一定会在main()函数中第一次用到identity之前,把identity

【深度探索c++对象模型】Function语义学之虚函数

虚函数的一般实现模型:每一个class有一个virtual table,内含该class之中有作用的virtual function的地址,然后每个object有一个vptr,指向virtual table. 识别class是否支持多态,唯一恰当的方法是看它是否有virtual function,只要class拥有virtual function,它就需要额外的执行期信息. 考虑ptr->z(),ptr是基类指针,z是虚函数,为了找到并调用z()的适当实体,我们需要两项信息: 1.ptr所指对象

《深度探索c++对象模型》chapter3 Data语意学

一个空的class:如 class X{} ; sizeof(X)==1; sizeof为什么为1,他有一个隐晦的1 byte,那是被编译器安插进去的一个char,这使得class2的两个objects得以在内存中配置独一无二的地址: X a,b; if(&a==&b) cerr<<"yipes!"<<endl; class X{}; class Y:public virtual X{}; class Z:public virtual X{};