关于对象


一、C++对象模型

在C++中,有两种数据成员:static和nonstatic,以及三种成员函数:static、nonstatic和virtual。已知下面这个类point声明:

在这里插入图片描述

怎样在机器中表现这个类point呢?

1.1、简单对象模型

在简单对象模型中,一个object是一系列的slots,每一个slot指向一个成员。成员按其声明顺序,各被指定一个slot。每一个数据成员或函数成员都有自己的一个slot。

在这里插入图片描述

在这个简单模型下,成员本身并不放在object中。只有"指向成员的指针"才放在object内。这么做可以避免"成员有不同的类型,因而需要不同的存储空间"所招致的问题。object中的成员是以slot的索引来寻址的。

虽然这个模型并没有被应用于实际产品上,不过关于索引或slot个数的观念,被应用到C++的"指向成员的指针"(pointer to member)观念之中。

1.2、表格驱动对象模型

为了对所有类的所有对象都有一致的表达方式,表格驱动对象模型把所有与成员相关的信息抽出来,放在一个数据成员表和一个成员函数表中,类对象本身则内含指向这两个表格的指针。

在这里插入图片描述

虽然这个模型也没有实际应用于真正的C++编译器身上,但成员函数表这个观念却成为支持虚函数的一个有效方案。

1.3、C++对象模型

Stroustrup当初设计(目前仍占有优势)的C++对象模型是从简单对象模型派生而来的,并对内存空间和存取时间做了优化。在此模型中,nonstatic数据成员被配置于每一个类对象之内,static数据成员则被存放在类对象之外。static和nonstatic成员函数也被放在类对象之外。虚函数则以两个步骤支持:

  • 1、每一个类产生出一堆指向虚函数的指针,放在表格中,这个表格被称为virtual table(vtbl)。
  • 2、每一个类对象被安插一个指针,指向相关的virtual table。通常这个指针被称为vptr。每一个类所关联的type_info 对象也经由virtual table被指出来,通常放在此表格的第一个slot。

在这里插入图片描述

1.4、对象模型如何影响程序

不同的对象模型,会导致"现有的程序代码必须修改"以及"必须加入新的程序代码"两个结果。例如下面这个函数,其中class X定义了一个copy constructor、一个virtual destructor和一个virtual function foo:

在这里插入图片描述

这个函数有可能在内部被转化为:

在这里插入图片描述

由于类X有两个virtual functions,一个是destructor,一个是foo,所以X对象布局如下:

在这里插入图片描述
在被转化的代码中,px->_vtbl[0]指向X的type_info object,px->_vtbl[1]指向X::~X(),px->_vtbl[2]指向X::foo()。

二、对象的差异

需要多少内存才能够表现一个类对象呢?一般而言要有:

  • 其nonstatic数据成员的总和大小
  • 加上任何由于alignment的需求而填补上去的空间(可能存在于成员之间,也可能存在于集合体边界)
  • 加上为了支持virtual而由内部产生的任何额外负担

2.1、指针的类型

一个指针,不管它指向哪一种数据类型,指针本身所需的内存大小是固定的。例如,有下面的ZooAnimal声明及变量定义:

在这里插入图片描述

其中的类对象za和指针pza的可能布局如下图所示:

在这里插入图片描述

但是,一个指向ZooAnimal的指针与一个指向整数的指针或一个指向template Array的指针有何不同呢?

在这里插入图片描述

以内存需求的观点来说,没有什么不同!它们三个都需要有足够的内存来放置一个机器地址。"指向不同类型之各指针"间的差异,既不在其指针表示法不同,也不在其内容(代表一个地址)不同,而是在其所寻址出来的对象类型不同。也就是说,"指针类型"会教导编译器如何解释某个特定地址中的内存内容及其大小

  • 一个指向地址1000的整数指针,在32位机器上,将涵盖地址空间1000~1003
  • 如果String是传统的8-bytes,那么一个ZooAnimal指针将横跨地址空间1000~1015

那么,一个指向地址1000而类型为void的指针,将涵盖怎样的地址空间呢?答案是不知道!这就是为什么一个类型为void的指针只能够持有一个地址,而不能够通过它操作所指对象的缘故。

2.2、加上多态

现在,让我们定义一个Bear,作为一种ZooAnimal。有如下类型定义和变量定义:

在这里插入图片描述

无论是pointer或reference都只需要一个word的空间(在32位机器上是4-bytes)。b、pb、rb的布局如下图所示。Bear object需要24bytes,也就是ZooAnimal的16bytes加上Bear所带来的8bytes。

在这里插入图片描述

假设我们的Bear object放在地址1000处,一个Bear指针和一个ZooAnimal指针有什么不同呢?

在这里插入图片描述

它们每个都指向Bear object的第一个byte。其间的差别是,pb所涵盖的地址包含整个Bear object,而pz所涵盖的地址只包含Bear object中的ZooAnimal subobject

除了ZooAnimal subobject中出现的成员,不能够使用pz来直接处理Bear的任何成员。唯一例外是通过virtual机制。

多态所造成的"一个以上的类型"的潜在力量,并不能够实际发挥在"直接存取objects"这件事情上,比如,将一个派生类对象赋值给一个基类对象时,派生类对象将会被切割以塞入较小的基类类型内存中。有一个似是而非的观念:面向对象程序设计并不支持对对象的直接处理。举个例子,下面这组定义:

在这里插入图片描述

其可能的内存布局如下图所示:

在这里插入图片描述

将za或b的地址,或pp所含的内容(也是个地址),指定给pza,显然不是问题。一个pointer或一个reference之所以支持多态,是因为它们并不引发内存中任何"与类型有关的内存委托操作(type-dependent commitment)";会受到改变的,只有它们所指向的内存的"大小和内容解释方式"而已。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值