zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++ 对象的内存布局(上)

2023-09-14 08:59:44 时间

4)多重继承(继承多个类)

5)重复继承(继承的多个父类中其父类有相同的超类)

6)虚拟继承(使用virtual方式继承,为了保证继承后父类的内存布局只会存在一份)

上述的东西通常是C++这门语言在语义方面对对象内部的影响因素,当然,还会有编译器的影响(比如优化),还有字节对齐的影响。在这里我们都不讨论,我们只讨论C++语言上的影响。

本篇文章着重讨论下述几个情况下的C++对象的内存布局情况。

1)单一的一般继承(带成员变量、虚函数、虚函数覆盖)

2)单一的虚拟继承(带成员变量、虚函数、虚函数覆盖)

3)多重继承(带成员变量、虚函数、虚函数覆盖)

4)重复多重继承(带成员变量、虚函数、虚函数覆盖)

5)钻石型的虚拟多重继承(带成员变量、虚函数、虚函数覆盖)

我们的目标就是,让事情越来越复杂。

我们简单地复习一下,我们可以通过对象的地址来取得虚函数表的地址,如:

 typedef void(*Fun)(void);

 Base b;

 Fun pFun = NULL;

 cout "虚函数表地址:" (int*)( b) endl;

 cout "虚函数表 — 第一个函数地址:" (int*)*(int*)( b) endl;

 // Invoke the first virtual function 

 pFun = (Fun)*((int*)*(int*)( b));

 pFun();

我们同样可以用这种方式来取得整个对象实例的内存布局。因为这些东西在内存中都是连续分布的,我们只需要使用适当的地址偏移量,我们就可以获得整个内存对象的布局。

本篇文章中的例程或内存布局主要使用如下编译器和系统:

1)Windows 7 和 VC++ 2010

2)Cygwin 和 G++ 3.4.4

单一的一般继承

下面,我们假设有如下所示的一个继承关系:

请注意,在这个继承关系中,父类,子类,孙子类都有自己的一个成员变量。而了类覆盖了父类的f()方法,孙子类覆盖了子类的g_child()及其超类的f()。

我们的源程序如下所示:

#include iostream 

using namespace std;

class Parent {

public:

 int iparent;

 Parent ():iparent (10) {}

 virtual void f() { cout " Parent::f()" endl; }

 virtual void g() { cout " Parent::g()" endl; }

 virtual void h() { cout " Parent::h()" endl; }

class Child : public Parent {

public:

 int ichild;

 Child():ichild(100) {}

 virtual void f() { cout "Child::f()" endl; }

 virtual void g_child() { cout "Child::g_child()" endl; }

 virtual void h_child() { cout "Child::h_child()" endl; }

class GrandChild : public Child{

public:

 int igrandchild;

 GrandChild():igrandchild(1000) {}

 virtual void f() { cout "GrandChild::f()" endl; }

 virtual void g_child() { cout "GrandChild::g_child()" endl; }

 virtual void h_grandchild() { cout "GrandChild::h_grandchild()" endl; }

int _tmain(int argc, _TCHAR* argv[])

 typedef void(*Fun)(void);

 GrandChild gc;

 Fun pFun=NULL;

 int** pVtab = (int**) 

 cout "[0] GrandChild::_vptr- " endl;

 /*for (int i=0; (Fun)pVtab[0][i]!=NULL; ++i){*/ 

 //本人注释 测试环境win7+VS2010 经过调试发现此环境下虚函数并不是以NULL结尾,所以原文修改为如下:

 for (int i=0; i ++i){

 pFun = (Fun)pVtab[0][i];

 cout " [" i "] ";

 pFun();

 cout "[1] Parent.iparent = " (int)pVtab[1] endl;

 cout "[2] Child.ichild = " (int)pVtab[2] endl;

 cout "[3] GrandChild.igrandchild = " (int)pVtab[3] endl;

 return 0;

image

使用图片表示如下:(红色部分为本人修改)

image

可见以下几个方面:

1)虚函数表在最前面的位置。

2)成员变量根据其继承和声明顺序依次放在后面。

3)在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。

下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类只overwrite了父类的f()函数,而还有一个是自己的函数(我们这样做的目的是为了用g1()作为一个标记来标明子类的虚函数表)。而且每个类中都有一个自己的成员变量:

我们的类继承的源代码如下所示:父类的成员初始为10,20,30,子类的为100

#include iostream 

using namespace std;

class Base1 {

public:

 int ibase1;

 Base1():ibase1(10) {}

 virtual void f() { cout "Base1::f()" endl; }

 virtual void g() { cout "Base1::g()" endl; }

 virtual void h() { cout "Base1::h()" endl; }

class Base2 {

public:

 int ibase2;

 Base2():ibase2(20) {}

 virtual void f() { cout "Base2::f()" endl; }

 virtual void g() { cout "Base2::g()" endl; }

 virtual void h() { cout "Base2::h()" endl; }

class Base3 {

public:

 int ibase3;

 Base3():ibase3(30) {}

 virtual void f() { cout "Base3::f()" endl; }

 virtual void g() { cout "Base3::g()" endl; }

 virtual void h() { cout "Base3::h()" endl; }


virtual void f() { cout "Derive::f()" endl; } virtual void g1() { cout "Derive::g1()" endl; } int _tmain(int argc, _TCHAR* argv[]) typedef void(*Fun)(void); Derive d; Fun pFun=NULL; int** pVtab = (int**) cout "[0] Base1::_vptr- " endl; pFun = (Fun)pVtab[0][0]; cout " [0] "; pFun(); pFun = (Fun)pVtab[0][1]; cout " [1] ";pFun(); pFun = (Fun)pVtab[0][2]; cout " [2] ";pFun(); pFun = (Fun)pVtab[0][3]; cout " [3] "; pFun(); pFun = (Fun)pVtab[0][4]; cout " [4] "; cout pFun endl; cout "[1] Base1.ibase1 = " (int)pVtab[1] endl;

我们通过上面的程序来查看子类实例的内存布局:上面程序中,注意我使用了一个s变量,其中用到了sizof(Base)来找下一个类的偏移量。(因为我声明的是int成员,所以是4个字节,所以没有对齐问题。关于内存的对齐问题,大家可以自行试验,我在这里就不多说了)

image

使用图片表示是下面这个样子:

image

我们可以看到:

1) 每个父类都有自己的虚表。

2) 子类的成员函数被放到了第一个父类的表中。

3) 内存布局中,其父类布局依次按声明顺序排列。

4) 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。


来源:http://www.oschina.net/translate/cpp-virtual-inheritance 来源:http://www.cnblogs.com/BeyondAnyTime/archive/2012/06/05/2537451.html C++中的虚拟继承的一些总结
07年12月,我写了一篇《C++虚函数表解析》的文章,引起了大家的兴趣。有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为,在上一篇文章中,我用了的示例都是非常简单的,主
C++继承中的内存布局 今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的。看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用。 ——谈VC++对象模型(美)简.格雷程化    译 一个C++程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节。