面试中经常遇到类似多态,虚继承,RTTI,dynamic_cast实现原理之类的问题,这块需要对C++底层内存模型比较理解。C++由于没有存储对象元信息(java反射基础),要支持多态,多继承特性,导致C++对象内存模型异常复杂。
一. 继承内存模型
1. 基类
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)
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信息,有则返回该基类实例。由此可见,该操作符会有一定性能损耗。
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,与书上描述不符。
参考链接:
相关推荐
C++对象内存模型.pdf
深度探索C++对象模型,内容概要:多态是一种威力强大的设计机制,允许你继承一个抽象的public接口之后,封装相关的类型,需要付出的代价就是额外的间接性--不论是在内存的获得,或是在类的决断上,C++通过class的pointer...
C++对象模型在内存中的实现,讲述了类,继承以及虚继承的内存布局;成员变量和成员函数的访问已经访问时的开销情况,包含虚函数的情况,考察构造函数,析构函数,以及特殊的赋值操作符成员函数是如何工作的,数组是...
很好的一本书。对于理解c++对象内存模型很有帮助。
网上看到的一篇博客,对C++对象内存模式讲的详细易懂。
C++对象模型(Th e C++ Object Model) 对象模型如何影响程序(How the Object Model Effects Programs) 1.2 关键词所带来的差异(A Keyword Distinction) 关键词的困扰 策略性正确的struct(The Politically ...
C++深度对象模型,讨论了很多C++对象内存模式和编译器处理,是深入对象编程的必读书。
C++对象模型(Th e C++ Object Model) 对象模型如何影响程序(How the Object Model Effects Programs) 1.2 关键词所带来的差异(A Keyword Distinction) 关键词的困扰 策略性正确的struct(The Politically ...
《深度探索C++对象模型》重点探索"对象导向程序所支持的C++对象模型"下的程序行为。对于"对象导向性质之基础实现技术"以及"各种性质背后的隐含利益交换"提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供...
验证C++对象模型,对C++中类及对象的内存布局做了详细说明,并使用代码验证模型
详细介绍C++ 对象模型, 对象内存布局
侯捷-深度探索C++对象模型 1:C++ 对象模型 2: C++ 对象内存分布 3: 函数,构造函数,虚构函数的编译与运行时语义的模型构建
对C++模型的认识可以从本质上提高对语言和各种机制的理解,如果对底层机制一无所知,那么很多高级的机制都只能通过死记硬背的方式来运用,而且有时候有错误,也很难找出原因。C++相对与C语言,编译器做了很多的对...
C++模型之菱形虚拟继承中的内存分布
c++对象模型,详细解析编译器对c++的OO结构在内存中的处理和排布机制
看过c++对象模型再来看这个,详细的分析vc中对象在内存的表示方式
本书重点:探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序...
1)有成员变量的情况。 2)有重复继承的情况。 3)有虚拟继承的情况。 4)有钻石型虚拟继承的情况。