C++ 对象内存模型_C/C++_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > C/C++ > C++ 对象内存模型

C++ 对象内存模型

 2019/1/8 20:32:26  tcspecial  程序员俱乐部  我要评论(0)
  • 摘要:面试中经常遇到类似多态,虚继承,RTTI,dynamic_cast实现原理之类的问题,这块需要对C++底层内存模型比较理解。C++由于没有存储对象元信息(java反射基础),要支持多态,多继承特性,导致C++对象内存模型异常复杂。一.继承内存模型1.基类structB{longb;virtualvoidfoo(){}virtualvoidbar(){}};gcc的-fdump-class-hierarchy选项,它可以用于输出C++程序的虚表结构g++-fdump-class
  • 标签:c++ 内存

?

面试中经常遇到类似多态,虚继承,RTTI,dynamic_cast实现原理之类的问题,这块需要对C++底层内存模型比较理解。C++由于没有存储对象元信息(java反射基础),要支持多态,多继承特性,导致C++对象内存模型异常复杂。

?

一. 继承内存模型

?

1. 基类

class="cpp">struct B
{
    long b;
    virtual void foo(){}
    virtual void bar(){}
};

?

gcc的-fdump-class-hierarchy选项,它可以用于输出C++程序的虚表结构

g++ -fdump-class-hierarchy -fsyntax-only test.c,在当前目录生成test.c.002t.class class文件。

?

结构分析:

cat test.c.002t.class 
Vtable for B
B::_ZTV1B: 4u entries
0     (int (*)(...))0				// offset
8     (int (*)(...))(& _ZTI1B)			// RTTI typeinfo
16    B::foo					// virtual table
24    B::bar

Class B
   size=16 align=8
   base size=16 base align=8
B (0x7ffa3355aaf0) 0
    vptr=((& B::_ZTV1B) + 16u)

?

B object

?

2. 多重继承

struct A
{
	int a;	
};

struct B : public A
{
    virtual void foo(){}
};

struct C : public A
{
    virtual void bar(){}
};


struct D: public B, public C
{
    virtual void qux(){}
};

// 测试
D d;

?

gdb调试查看对象成员:

(gdb) p d
$1 = {<B> = {<A> = {a = 4195632}, _vptr.B = 0x400830}, <C> = {<A> = {a = 0},_vptr.C = 0x400850}, <No data fields>}
(gdb) p sizeof(d)
$2 = 32
(gdb) x/32xb &d
0x7fffffffdf10: 0x30    0x08    0x40    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf18: 0x30    0x05    0x40    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf20: 0x50    0x08    0x40    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf28: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

?

B 和 C各自拥有一个A成员,sizeof(D) = 4(+4padding) + 8 + 4(+4padding) + 8 = 32,D有两个A成员,对象尺寸膨胀,同时代码对基类成员引用容易出现二义性。

多重继承对象?

?

3. 虚继承

struct B : virtual public A
{
    virtual void foo(){}
};

struct C : virtual public A
{
    virtual void bar(){}
};

?

gdb调试:

(gdb) p d
$1 = {<B> = {<A> = {a = -8176}, _vptr.B = 0x400838}, <C> = {_vptr.C = 0x400860}, <No data fields>}
(gdb) p sizeof(d)
$2 = 24
(gdb) x/24xb &d
0x7fffffffdf10: 0x38    0x08    0x40    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf18: 0x60    0x08    0x40    0x00    0x00    0x00    0x00    0x00
0x7fffffffdf20: 0x10    0xe0    0xff    0xff    0xff    0x7f    0x00    0x00

?

虚继承

?

二. dynamic_cast 实现

Derive *d = dynamic_cast<Derive*>(base);

?

从上图可知,虚表中包含typeinfo信息,dynamic_cast会查找虚表中是否包含Derive 的RTTI信息,有则返回该基类实例。由此可见,该caozuofu.html" target="_blank">操作符会有一定性能损耗。

?

RTTI 信息是包含在虚表内,如果没有virtual 函数,则不会生成虚表,此时dynamic_cast 转换会怎样?

test.c:34: error: cannot dynamic_cast ‘base’ (of type ‘class Base*’) to type ‘class Derive*’ (source type is not polymorphic)

?结果编译出错。?

?

?

三. 多重继承对象大小

// 基类
class Concrete1
{
public:
	int val;
	char c1;
};

class Concrete2: public Concrete1
{
public:
	char c2;	
};

class Concrete3: public Concrete2
{
public:
	char c3;	
};

// 测试
Concrete3 c3; 
c3.val = 1;
c3.c1 = 'A';
c3.c2 = 'B';
c3.c3 = 'C';

?

sizeof(Concrete3) != 16?

按照《深入探索C++对象模型》书里介绍,Concrete2/Concrete3均会添加padding,因此sizeof为16。我们在gcc 4.4.7上验证下该问题。

(gdb) p c3
$1 = {<Concrete2> = {<Concrete1> = {val = 1, c1 = 65 'A'}, c2 = 66 'B'}, c3 = 67 'C'}
(gdb) p sizeof(c3)
$2 = 12
(gdb) x/12xb &c3
0x7fffffffde60: 0x01    0x00    0x00    0x00    0x41    0x7f    0x00    0x00
0x7fffffffde68: 0x42    0x43    0x60    0x00

?

c3使用了padding区域,总长度因此为12,与书上描述不符。

?

?

?

参考链接:

深入虚表结构

dynamic_cast运行原理

虚函数与虚继承寻踪

?

?

?

?

  • 大小: 8.5 KB
  • 大小: 19.5 KB
  • 大小: 21.5 KB
  • 查看图片附件
发表评论
用户名: 匿名