深度探索C++对象模型之一 --- 关于对象

来源:互联网 发布:企业如何进行网络推广 编辑:程序博客网 时间:2024/06/11 16:27

深度探索C++对象模型之一 — 关于对象

使用封装之后的成本

从软件工程的眼光来看,“一个ADT或class hierarchy的数据封装”比“在C程序中程序性地使用全局数据”好。

加上封装之后,并没有增加布局成本。data member直接内含在每一个class object之中,而member functions虽然在class中声明,但是却不出现在object之中。而non-inline member function只会诞生一个函数实例。至于inline-function则会在每一个使用的地方产生一个函数实体。所以封装并没有增加object的布局成本,在布局和存取时间上的额外负担主要是有virtual引起的,包括:

  1. virtual function机制 用以支持一个有效率的“执行期绑定”
  2. virtual base class 用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实例”

c++对象模式

在c++中,有两种class data member:static和nonstatic,有三种class member function:static、nonstatic和virtual。

那么对于一个对象来说,如果上面的集中members的话,一个对象是如何在内存中布局的呢?历史上一共出现过三种对象模式,下面逐次介绍。

  1. 简单对象模型
    一个对象包含多个slots,每一个slot都指向一个data member或member function,因为每个slot包含的是一个指针,不是存储真正的数据类型,那么所占的空间就是指针的大小(4Bytes),因此object的大小就很容易算出来。(引入slot的概念)

  2. 表格驱动对象模型
    这种模型把class的members抽象成两种表,一张表存储data member,另外一张存储member function。而每个obejct中有两个slot,一个slot指向data member table,另一个则指向memer function table。(引入member function table的概念)

  3. C++对象模型
    在这种对象模型中,nonstati data members被置于每一个class object中拥有自己的value,而static data members、static member functions以及nonstatic member functions都被放置在class object之外。而virtual function则是按照如下步骤实现:(1)在编译期间,编译器被该class产生一个virtual table,里面专门存放指向virtual functions的指针 (2)为每一个class object添加一个vptr指针,专门指向virtual table。vptr的设置都是由构造函数、析构函数、拷贝构造函数等自动完成。每一个class用来支持runtime type idetification的type_info object一般都会被存放在virtual table的第一个slot中。如下图所示:

    如果class是出在一个继承链当中那么一个class object又该如何布局呢?c++对象模型会将base class subobject的data members直接放置于derived class object中,而对于虚拟继承的virtual base class,因为无论在继承链中继承多少次,最后都只会有一个实体。Virtual base class的原始模型是在class object中为每一个有关联的virtual base class加上一个指针,直接指向virtual base class

单继承

  1. 子类没有重写的父类虚函数,就直接使用父类虚函数
  2. 子类覆盖父类的虚函数
  3. 子类新增的虚函数
多重继承

  1. 新增的虚函数放在第一个父类虚函数中
  2. 重写的虚函数要对所有类的虚函数都覆盖
  3. 内存布局中父类按照声明的顺序排列
简单虚拟继承

简单虚拟继承中derived class 新增的虚函数放在自己的vtbl中,增加一个vbptr指向virtual base class

菱形虚拟继承

菱形虚拟继承中derived class新增的虚函数仍然是放在第一个父类的vtbl中,每一个父类都有一个vbptr指向virtual base class,父类中不存放virtual base class的data members。

struct和class的关系

c++不区分都可以用,只是class封装的哲学理念。另外struct对于数据结构的尾端可以存放可变大小的数组,而class因为布局以及virtual机制的影响,不一定能够支持这种。如果要传递“一个复杂的class object的全部或部分”到某个C函数时,struct声明可以将数据封装起来,并保证拥有与C兼容的空间布局。

只有通过pointer或reference的间接处理才支持多态。c++以下列方法支持多态:

  1. 经由一组隐式的转化操作。如把一个derived class指针转化成一个指向其public base class指针 Shape *ps = new Circle();
  2. 经由virtual function机制: ps->rotate();
  3. 经由dynamic_cast和typeid运算符:
    if(circle *pc = dynamic_cast<circle *>(ps)) ...

class object大小

  1. nonstatic data member的总和大小
  2. 边界对其补充的大小
  3. 为了支持virtual而产生的内部负担

指针的类型

“指向不同类型的指针”的差异,既不在指针表示法不同,也不在其内容不同,而时在其所寻址出来的object类型不同。“指针类型”教导编译器如何解释某个特定地址中的内存内容及其大小。

转换(cast)其实是一种编译器指令,大部分情况下并不改变一个指针所含的真正的地址,而只影响“被指出内存的大小和其内容”的解释方式。

static_cast<> 和dynamic_cast<>

static_cast<?>()可以用于在相关指针之间转换,void*指针间转换,还可以在基类和派生类之间转化,因为是在编译期间确定下来的,所以设计者必须自己确保安全性。

dynamic_cast<?>()专门用于具有继承关系的类之间转换

0 0
原创粉丝点击