实用C++内存分配详解,看完可以写C++编译器了

上传人:M****1 文档编号:542907840 上传时间:2022-10-23 格式:DOC 页数:22 大小:622KB
返回 下载 相关 举报
实用C++内存分配详解,看完可以写C++编译器了_第1页
第1页 / 共22页
实用C++内存分配详解,看完可以写C++编译器了_第2页
第2页 / 共22页
实用C++内存分配详解,看完可以写C++编译器了_第3页
第3页 / 共22页
实用C++内存分配详解,看完可以写C++编译器了_第4页
第4页 / 共22页
实用C++内存分配详解,看完可以写C++编译器了_第5页
第5页 / 共22页
点击查看更多>>
资源描述

《实用C++内存分配详解,看完可以写C++编译器了》由会员分享,可在线阅读,更多相关《实用C++内存分配详解,看完可以写C++编译器了(22页珍藏版)》请在金锄头文库上搜索。

1、一个C+程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节。对于使用VC+的程序员来说,还应该了解一些 VC+对于C+的诠释。Inside the C+ Object Model 虽然是一本好书,然而,书的篇幅多一些,又和具体的VC+关系小一些。因此,从篇幅和内容来看,译者认为本文是深入理解C+对象模型比较好的一个岀发点。这篇文章以前看到时就觉得很好,旧文重读,感觉理解得更多一些了,于是产生了翻译岀来,与大家共享的想法。虽然文章不长,但时间有限,又若干次在翻译时打盹睡着,拖拖拉拉用了小一个月。一方面因本人水平所限,另一方面因翻译时经常打盹,错误之处恐怕不少,欢迎大家批评指正。前言

2、了解你所使用的编程语言究竟是如何实现的,对于C+程序员可能特别有意义。首先,它可以去除我们对于所使用语言的神秘感,使我们不至于对于编译器干的活感到完全不可思议;尤其重要的是,它使我们在Debug和使用语言高级特性的时候,有更多的把握。当需要提高代码效率的时候,这些知识也 能够很好地帮助我们。本文着重回答这样一些问题:*类如何布局?*成员变量如何访问?*成员函数如何访问?*所谓的 调整块” vadjuster thunk)是怎么回事?*使用如下机制时,开销如何:*单继承、多重继承、虚继承*虚函数调用*强制转换到基类,或者强制转换到虚基类*异常处理首先,我们顺次考察 C兼容的结构vstruct )

3、的布局,单继承,多重继承,以及虚继承;接着,我们 讲成员变量和成员函数的访问,当然,这里面包含虚函数的情况;再接下来,我们考察构造函数,析构函数,以及特殊的赋值操作符成员函数是如何工作的,数组是如何动态构造和销毁的;最后,简单地介绍 对异常处理的支持。对每个语言特性,我们将简要介绍该特性背后的动机,该特性自身的语意struct A char c。int i。译者注:从上图可见,A在内存中占有8个字节,按照声明成员的顺序,前 4个字节包含一个字符实际占用1个字节,3个字节空着,补对齐),后 4个字节包含一个整数。A的指针就指向字符开始字节处。2、有C+特征的C结构当然了,C+不是复杂的C, C+

4、本质上是面向对象的语言:包含继承、封装,以及多态。原始的C结构经过改造,成了面向对象世界的基石一一类。除了成 员变量外,C+类还可以封装成员函数和其他东西。然而,有趣的是,除非为了实现虚函数和虚继承引入的隐藏成员变量外,C+类实例的大小完全取决于一个类及其基类的成员变量!成员函数基本上不影响类实例的大小。这里提供的B是一个C结构,然而,该结构有一些C+特征:控制成员可见 性的“public/protected/private关键字”成员函数、静态成员,以及嵌套的类型声明。虽然看着琳琅满目,实际 上只有成员变量才占 用类实例的空间。要注意的是,C+标准委员会不限制由“public/protect

5、ed/private关键字分开的各段在实现时的先后顺序,因此,不同的编译器实现的内存布局可能并不相同。 。static void bsf( 。typedef void* bpv 。struct N 。译者注:B中,为何static int bsm不占用内存空间?因为它是静态成员,该数据存放在程序的数据段中,不在类实例中。3、单继承C+提供继承的目的是在不同的类型之间提取共性。比如,科学家对物种进行分类,从而有种、属、纲等说法。有了这种层次结构,我们才可能将某些具备特定性质的东西归入到最合适的分类层次鲸鱼、人”是哺乳动上,如 怀孩子的是哺乳动物”。因为这些属性可以被子类继承,所以,我们只要知道

6、物,就可以方便地 指出 鲸鱼、人都可以怀孩子”。那些特例,如鸭嘴兽 生蛋的哺乳动物),则要求我们 对缺省的属性或行为进行覆盖。C+中的继承语法很简单,在子类后加上“:base就可以了。下面的 D继承自基类C。C: : clstruct C int cl 。void cf( 。 。C: : alDi : distruct D : C int d1 。void df( 。 。既然派生类要保留基类的所有属性和行为,自然地,每个派生类的实例都包含了一份完整的基类实 例数据。在D中,并不是说基类 C的数据一定要放在 D的数据之前,只不过这样放的话,能够保证 D中 的C对象地址,恰好是 D对象地址的第一个

7、字节。这种安排之下,有了派生类D的指针,要获得基类 C的指针,就不必要计算 偏移量了。几乎所有知名的 C+厂商都采用这种内存安排。在单继承类层次下, 每一个新的派生类都简单地把自己的成员变量添加到基类的成员变量之后。看看上图,C对象指针和D对象指针指向同一地址。4、多重继承大多数情况下,其实单继承就足够了。但是,C+为了我们的方便,还提供了多重继承。比如,我们有一个组织模型,其中有经理类 分任务),工人类 干活)。那么,对于一线经理类, 即既要从上级经理那里领取任务干活,又要向下级工人分任务的角色来说,如何在类层次中表达呢?单继承在此就有点力不胜任。我们可以安排经理类先继承工人类,一线经理类再

8、继承经理类,但这种层次结 构错误地让经理类继承了工人类的属性和行为。反之亦然。当然,一线经理类也可以仅仅从一个类经理类或工人类)继承,或者一个都不继承,重新声明一个或两个接口,但这样的实现弊处太多:多态不可能了;未能重用现有的接口;最严重的是,当接口变化时,必须多处维护。最合理的情况似乎是一线经理 从两个地方继承属性和行为一一经理类、工人类。C+就允许用多重继承来解决这样的问题:struct Manager . . 。struct Worker . . 。struct MiddleManager : Manager, Worker . 这样的继承将造成怎样的类布局呢?下面我们还是用字母类”来举

9、例:E: : elstruct E int e1 。void ef(。struct F : C, E int f1。void ff( 。结构F从C和E多重继承得来。与单继承相同的是,F实例拷贝了每个基类的所有数据。与单继承不同的是,在多重继承下,内嵌的两个基类的对象指针不可能全都与派生类对象指针相同:F f。/ (void*& f = (void*(C* &f。/(void*&f (E* &f。译者注:上面那行说明 C对象指针与F对象指针相同,下面那行说明E对象指针与F对象指针不同。观察类布局,可以看到 F中内嵌的E对象,其指针与F指针并不相同。正如后文讨论强制转化和成 员函数时指岀的,这个偏

10、移量会造成少量的调用开销。具体的编译器实现可以自由地选择内嵌基类和派生类的布局。VC+按照基类的声明顺序先排列基类实例数据,最后才排列派生类数据。当然,派生类数据本身也是按照声明顺序布局的 本规则并非一成不变,我们会看到,当一些基类有虚函数而另一些基类没有时,内存布局并非如此)。5、虚继承回到我们讨论的一线经理类例子。让我们考虑这种情况:如果经理类和工人类都继承自雇员类”,将 会发生什么?struct Employee . 。struct Manager : Employee . 。struct Worker : Employee . 。struct MiddleManager : Manag

11、er, Worker . 如果经理类和工人类都继承自雇员类,很自然地,它们每个类都会从雇员类获得一份数据拷贝。如 果不作特殊处理,一线经理类的实例将含有两个雇员类实例,它们分别来自两个雇员基类。如果雇员类成员变量不多,问题不严重;如果成员变量众多,则那份多余的拷贝将造成实例生成时的严重开销。更糟 的是,这两份不同 的雇员实例可能分别被修改,造成数据的不一致。因此,我们需要让经理类和工人类 进行特殊的声明,说明它们愿意共享一份雇员基类实例数据。很不幸,在C+中,这种共享继承”被称为虚继承”,把问题搞得似乎很抽象。虚继承的语法很简 单,在指定基类时加上 virtual关键字即可。struct Em

12、ployee . 。struct Manager : virtual Employee . struct Worker : virtual Employee . struct MiddleManager : Manager, Worker . 使用虚继承,比起单继承和多重继承有更大的实现开销、调用开销。回忆一下,在单继承和多重继承的情况下,内嵌的基类实例地址比起派生类实例地址来,要么地址相同 单继承,以及多重继承的最靠左基类),要么地址相差一个固定偏移量多重继承的非最靠左基类)。然而,当虚继承时,一般说来,派生类地址和其虚基类地址之间的偏移量是不固定的,因为如果这个派生类又被进一步继承的话,最

13、终 派生类会把共享的虚基类实例数据放到一个与上一层派生类不同的偏移量处。请看下例:译者注:GdGvbptrGvIn G, the displacement of G s virtual base pointer to G )意思是:在 G 中,G 对象的指针与G的虚基类表指针之间的偏移量,在此可见为0,因为G对象内存布局第一项就是虚基类表指针; GdGvbptrCln G, the displacement of G s virtual base pointer to C )意思是:在 G 中,C 对象 的指针与G的虚基类表指针之间的偏移量,在此可见为4。)HdHvbptrHHdHvbptrC暂时不追究vbptr成员变量从何而来。从上面这些图可以直观地看到,在G对象中,内嵌的C基类对象的数据紧跟在 G的数据之后,在 H对象中,内嵌的C基类对象的数据也紧跟在 H的数据之后。但 是,在I对象中,内存布局就并非如此了。VC+实现的内存布局中,G对象实例中G对象和C对象之间的偏移,不同 于I对象实例中

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 活动策划

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号