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

释放双眼,带上耳机,听听看~!

 

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

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

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

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

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

单一的一般继承

 

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

 

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

 

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

 

我们的源程序如下所示:

 

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** pVtab 来作为遍历对象内存布局的指针,这样,我就可以方便地像使用数组一样来遍历所有的成员包括其虚函数表了,在后面的程序中,我也是用这样的方法的,请不必感到奇怪,)

 

    
typedef
 
void
(*Fun)(
void
);

    GrandChild gc;

   

 

    
int
** pVtab = (
int
**)&gc;

 

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

    
for
 (
int
 i=
0
; (Fun)pVtab[
0
][i]!=NULL; 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;

 

其运行结果如下所示:(在VC++ 2003和G++ 3.4.4下)

 

[0] GrandChild::_vptr->     [0] GrandChild::f()     [1] Parent::g()     [2] Parent::h()     [3] GrandChild::g_child()     [4] Child::h1()     [5] GrandChild::h_grandchild() [1] Parent.iparent = 10 [2] Child.ichild = 100 [3] GrandChild.igrandchild = 1000

 

使用图片表示如下:

 

 

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

 

可见以下几个方面:

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

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

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

多重继承

 

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

 

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

 

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

 

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; }

};

 

 

class
 Derive : 
public
 Base1, 
public
 Base2, 
public
 Base3 {

public
:

    
int
 iderive;

    Derive():iderive(
100
) {}

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

    
virtual
 
void
 g1() { cout << 
"Derive::g1()"
 << endl; }

};

 

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

 

             
typedef
 
void
(*Fun)(
void
);

               Derive d;

 

                
int
** pVtab = (
int
**)&d;

 

                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;

 

 

**                int
 s = 
sizeof
(Base1)/
4
;**

 

                cout << 
"["
 << s << 
"] Base2::_vptr->"
<<endl;

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

                cout << 
"     [0] "
; pFun();

 

                Fun = (Fun)pVtab[s][
1
];

                cout << 
"     [1] "
; pFun();

 

                pFun = (Fun)pVtab[s][
2
];

                cout << 
"     [2] "
; pFun();

 

                pFun = (Fun)pVtab[s][
3
];

                out << 
"     [3] "
;

                cout<<pFun<<endl;

 

                cout << 
"["
<< s+
1
 <<
"] Base2.ibase2 = "
 << (
int
)pVtab[s+
1
] << endl;

 

                **s = s + 
sizeof
(Base2)/
4
;
**

                cout << 
"["
 << s << 
"] Base3::_vptr->"
<<endl;

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

                cout << 
"     [0] "
; pFun();

 

                pFun = (Fun)pVtab[s][
1
];

                cout << 
"     [1] "
; pFun();

 

                pFun = (Fun)pVtab[s][
2
];

                cout << 
"     [2] "
; pFun();

 

                pFun = (Fun)pVtab[s][
3
];

                 cout << 
"     [3] "
;

                cout<<pFun<<endl;

 

                s++;

                cout << 
"["
<< s <<
"] Base3.ibase3 = "
 << (
int
)pVtab[s] << endl;

                s++;

                cout << 
"["
<< s <<
"] Derive.iderive = "
 << (
int
)pVtab[s] << endl;

 

其运行结果如下所示:(在VC++ 2003和G++ 3.4.4下)

[0] Base1::_vptr->      [0] Derive::f()      [1] Base1::g()      [2] Base1::h()      [3] Driver::g1()      [4] 00000000      ç 注意:在GCC下,这里是1 [1] Base1.ibase1 = 10 [2] Base2::_vptr->      [0] Derive::f()      [1] Base2::g()      [2] Base2::h()      [3] 00000000      ç 注意:在GCC下,这里是1 [3] Base2.ibase2 = 20 [4] Base3::_vptr->      [0] Derive::f()      [1] Base3::g()      [2] Base3::h()      [3] 00000000 [5] Base3.ibase3 = 30 [6] Derive.iderive = 100

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

 

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

我们可以看到:

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

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

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

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

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C/C++内存泄漏及检测

2022-1-11 12:36:11

安全运维

Docker数据管理

2021-12-12 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索