C++对象布局及多态实现的探索
前言 本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等。 写这篇文章源于我在论坛上看到的一个贴子。有人问VC使用了哪种方式来实现虚继承。当时我写了一点代码想验证一下,结果发现情况比我想象的要复杂。所以我就干脆认真把相关的问题都过了一遍,并记录成本文。 我对于C++对象模型的知识主要来自于Lippman的书《Inside the C++ Object Model》,中译版为候捷翻的《深度探索C++对象模型》,中英版我都看过,不过我还是推荐中译版,因为中译版的确翻得不错,而且候捷加入了很多的图,并修正了原版中的一些错误。 我所使用的编译器是VC7.1,文中的代码我都在VC7.1上验证通过。如果在其他的编译器下运行需要作相应的调整,即使是VC7.0和VC6也是如此。不同编译器产生的汇编代码也不一样,如果你在不同编译器上编译文中的代码生成出的汇编代码和我所列出的不同,也不足为奇。如果你想在其他的编译器上验证这些代码请自行做相应的改动。 另外我发现VC7.1在实现虚继承时所用的方法和Lippman在书中提到的微软所用的方法不同,不过那时还没有VC7.1.有趣的是,Lippman在写那本书时,是在迪斯尼工作,应该是做和三维影片的渲染软件相关的事。而现在他已经到了微软,相信应该是主导VC7.1编译器的设计工作。另外值得一提的是Herb,此人是C++标准委员会的一员,写过多本C++方面的经典书籍,现在也已经加入了微软。虽然我不是微软的“粉丝”,但对于VC不得不关注。VC8.0的beta版也已经出来了。 在后文中可以看到列出的很多汇编代码,有些明显效率很低。这可能是因为我没有打开编译器的优化开关。打开优化开关,设置不同的优化选项后,编译器可能产生出高效得多的汇编代码。有兴趣的朋友可以自行试试,并和文中列出的汇编代码做一下比较。 为了便于分析和观察对象的内存布局,我把代码生成时的结构成员对齐选项设置为1字节,默认为8字节。如果你在自己的工程下编译文中的代码,请做同样的设置。因为我写了一些函数打印对象中的布局信息,如果对象选项不是1字节,运行这些代码会出现指针异常错误。 文中所列出的代码可以从附件中下载到。代码所用到的宏的语义及参数说明,和代码中每一个类的简单描述可以在附录中找到。 普通类对象的内存布局 首先我们从普通类对象的内存布局开始。C000为一个空类,定义如下: struct C000 { }; 运行如下代码打印它的大小及对象中的内容。 PRINT_SIZE_DETAIL(C000) 结果为: The size of C000 is 1 The detail of C000 is cc 可以看到它的大小为1字节,这是一个占位符。我们可以看到它的值是0xcc.在debug模式下,这表示是由编译器插入的调试代码所初始化的内存。在release模式下可能是个随机值,我测试时值为0x00. 定义两个类,C010和C011如下:
运行如下代码打印它们的大小及对象中的内容。 PRINT_SIZE_DETAIL(C010) PRINT_SIZE_DETAIL(C012) 结果为: The size of C010 is 1 The detail of C010 is 01 The size of C011 is 2 The detail of C011 is 02 03 我们从对象的内存输出中可以看到,它们的值就是我们在构造函数中赋的值,C010为0x01,C011为0x0203.大小分别为1、2. 定义C012类。
在这个类中我们加入了一个静态数据成员,一个普通成员函数和一个静态成员函数。 运行如下代码打印它的大小及对象中的内容。 PRINT_SIZE_DETAIL(C012) |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |