一、C和C++对比:

C语言的Point3d: 数据成员定义在结构体之内,存在一组各个以功能为导向的函数中,共同处理外部的数据。

 typedef struct point3d
{
float x;
float y;
float z; }Point3d;

Point3d可能采用独立的“抽象数据类型”(abstract data type)来实现:  

 class Point3d
{
public:
Point3d(float x = 0.0,float y = 0.0,float z = 0.0)
:_x(x), _y(y), _z(z) {} float x() {return _x;}
float y() {return _y;}
float z() {return _z;} void x(float xval) {_x = xval;}
private:
float _x;
float _y;
float _z;
} inline ostream&
operator<<(ostream &os,const Point3d &pt)
{
os << pt.x()<<pt.y()<<pt.z();
}

、加上封装之后的布局成本:

  加上封装之后,布局成本增加了多少?    并没有增加布局成本。一般而言,并没有什么理由说C++程序一定比C庞大和迟缓。

  因为三个数据成员直接内含在每一个类对象中,而成员函数虽然含在class的声明之内,但是却不出现在对象中。每一个非内联成员函数只会诞生一个函数实例。至于“拥有零个或一个定义”的内联函数则会在每一个模块身上产生一个函数实例。

虽然C++的类含有封装特新,但是并没有给系统在占用空间和执行上带来不良后果。

C++在布局以及存取时间上的额外负担主要是由virtual引起的:

  • virtual function(虚函数)   用以支持一个有效率的“执行器绑定(runtime binding)”
  • virtual base class (虚基类)  用以实现“多次出现在继承体系中的 base class(基类),有一个单一而被共享的实例。”

还有一些其他的额外负担,比如一个派生类和它的第二或后继承的基类之间的转换。


三、C++对象模型:

  在C++中,有两种类数据成员:static  nonstatic;  三种类成员函数:static  nonstatic  virtual

例如下面这个class在机器中是怎么分布的呢,即如何modeling出各种数据成员和成员函数:

 class Point {
public:
Point(float xval) ;
virtual ~Point(); float x() const;
static int PointCount(); ptotected:
virtual ostream&
print(ostream &os )const ;
float _x;
static int _point_count;
};

分别有以下几个模型:

  • 简单对象模型

     每一个对象有是一系列的槽,每一个槽指向一个成员。成员按其声明排序。每一个数据成员或成员函数都有自己的一个槽。如下图所示:

在这种情况下,放在对象中的是指向成员的指针,这样可以避免成员有不同的类型,因而需要不同存储空间的问题。对象中的成员是以槽的索引值来寻址的。上图中_x的索引值是6._point_count的索引值是7。一个类对象的大小 = 指针大小 * 类中声明的成员个数。

  • 表格驱动模型

    表格对象模型是把所有与members相关的信息抽出来,放在一个数据成员表格和一个成员函数表格中,类对象本身只含有指向这两个表格的指针。成员函数表格是一系列的槽,每个槽指向一个成员函数。数据成员表格则直接包含数据成员本身。如下图所示:

虽然这个模型没有实际应用到C++编译器上,但是成员函数表格这个概念却成为支持虚函数的一个有效方法。

  • C++对象模型

    C++对象模型是从简单对象模型派生而来,并对内存空间和存取时间做了优化。在C++对象模型中,非静态数据成员被配置在每一个类对象中,静态数据成员被配置在类对象之外。静态和非静态成员函数也被放在类对象之外。虚函数的配置方式如下说明:

  1. 每个类产生一堆指向虚函数的指针,放在虚函数表中(virtual table,vtbl)
  2. 每个类对象中含有一个指向虚函数表的指针vptr。vptr的设定(setting)和重置(resetting)都由每个类的构造函数、析构函数、拷贝赋值函数自动完成。每个类关联的type_info 对象(用来支持 runtime type identification,RTTI)也由虚函数表指出来,通常放在虚函数表的第一个槽。

C++对象模型的优点是它的空间和时间效率高,缺点是类对象的非静态数据成员被修改(增加、移除等),那么程序就需要重新编译。相比前述的表格驱动模型就多了一层间接性,不过也付出了空间和执行效率上的耗费。


 四、在C++对象模型上加上继承(Inheritance)

  • 单一继承
 class Library_materials {....};
class Book : public Library_materials {...};
class Rental_book : public Book {...};
  • 多重继承
 class iostream:
public istream,
public ostream {...};
  • 虚拟继承
class istream : virtual public ios{...};
class ostream : virtual public ios {...};

在虚继承情况下,不管基类在继承串中派生(derived)多少次,都只有一个实例(subobject)。例如iostream中,只有virtual ios base class一个实例。

一个派生类如何模塑其基类的实例呢?

  • 在简单对象模型中:

    派生类对象中有一个槽指向基类,槽内含有基类实例(base class subobject)的地址。这种方法的主要缺点是因为间接性会导致空间和存取时间上的额外负担,优点是改变基类不会影响类对象的大小。

  • 在表格模型中:

每个类对象中内涵一个bptr,它会被初始化指向其基类表(base class table),和虚函数表中含有虚函数地址一样,在基类表格(base class table)中,每个槽内有一个相关的基类地址,。这种方法的缺点是:由于间接性导致空间和存取时间的额外负担;优点是:每个类对象中对于继承都有一致的表现:每个类对象都会在某个固定位置上放置一个基表格指针,与基类的大小或个数无关。第二个有点是:无序改变类对象的大小,就可以放大、缩小,或更改基类表格。

下图是基类表格在虚继承中的应用:

  

不管上述哪一种体制,“间接性”的级数都因为继承的深度而增加。例如Rental_book需要两次间接存取才能够探取到继承自Library_materials的成员,而Book只需要一次。如果在派生类内复制一个指针,指向继承串链中的每一个基类,倒是可以获得一个永远不变的存取时间,当然这也需要额外的空间来放置额外的指针。

C++最初采用的继承模型并不运用任何间接性,基类实例的数据成员被直接放置于派生类对象中,这种方法虽然可以很方便且最有效率的对基类成员存取。但缺点是基类成员有任何改变,所有用到该基类的派生类对象都必须重新编译。

从C++2.0起开始导入虚基类(virtual base class),虚基类的原始模型是在类对象中为每一个有关联的虚基类加上一个指针指向这些虚基类。其他派生的模型不是导入一个虚基类表格(Virtual base class table)就是扩充原有的虚表格(virtual table),以便维护每一个虚基类的位置。


五、对象模型如何影响程序:

  不同的对象模型,会导致两个结果:现有程序代码必须修改或者必须加入新的程序代码。

 X foobar()
{
X xx;
X *px = new X; // foo是一个虚函数
xx.foo();
px->foo(); delete px;
return xx;
}

上面这个类X定义了一个拷贝构造函数、虚析构函数、和一个虚函数foo,则这个函数有可能在内部转换为:

 void foobar(X &_result)
{
//构造_result
//_result 用来取代localxx
_result.X::X(); //扩展X *px = new X; new在自由存储区开辟了一块内存,px是位于栈内存中
px = _new(sizeof(X)); //px指向的对象开辟一块新内存
if(px != )
px->X::X(); //执行拷贝构造 //扩展xx.fo() 但是不适用virtual机制
//以_result 取代xx
foo(&_result); //使用virtual机制扩展px->fo()
(*px->vtbl[]) (px) //X.foo() //扩展delete px;
(*px ->vtbl[](px)); //析构函数
//无需使用named return statement
//无须摧毁local object xx
}

上述代码的理解如下所示:

深度探索C++对象模型之第一章:关于对象之C++对象模型的更多相关文章

  1. 《深度探索C++对象模型》第一章 | 关于对象

    C++对象模式 非静态数据成员放置在每个类对象内,静态数据成员则被放置在所有类对象之外.静态和非静态的成员函数也被放置在所有类对象之外.每个类产生一堆指向虚函数的指针,放在虚表(vtbl)中.每个类对 ...

  2. 【C++对象模型】第一章 关于对象

    1.C/C++区别 C++较之C的最大区别,无疑在于面向对象,C程序中程序性地使用全局数据.而C++采用ADT(abstract data tpye)或class hierarchy的数据封装.类相较 ...

  3. 深度探索C++对象模型之第一章:关于对象之对象的差异

    一.三种程序设计范式: C++程序设计模型支持三种程序设计范式(programming paradiams). 程序模型(procedural model) char boy[] = "cc ...

  4. 深度探索C++对象模型之第一章:关于对象之关键词所引起的差异

    ————如果不是为了努力维护与C之间的兼容性,C++远比现在简单的多. 如果一个程序员渴望学习C++,但是他却发现书中没有熟悉的struct,一定会苦恼,将这个主题包含到C++里,可以提供语言转移时的 ...

  5. Android深度探索-卷1第六章心得体会

    这章主要介绍了第一个linux驱动程序:统计单词个数.Linux系统将每一个驱动都映射成一个文件,这些文件称为设备文件或驱动文件,都保存在/dev目录中.大多数Linux驱动都有与其对应的设备文件,因 ...

  6. Android深度探索-卷1第七章心得体会

    创建LED驱动的设备文件 第一步:使用cdev_init函数初始化cdev 第二步:指定设备号.直接在代码指定或动态分配 第三步:使用cdev_add函数将字符设备添加到内核中的字符设备数组中 第四步 ...

  7. Android深度探索-卷1第五章心得体会

    S3C6410是由三星公司推出的一款低功耗.高性价比的RISC处理器,开发是,首先安装minicom串口调试工具: 第一步:检测当前系统是否支持USB转串口. Lsmod | grep usseria ...

  8. Android深度探索-卷1第四章心得体会

    这一章的和三章的git用法有联系,so,吧上一章的git基本用法搞好了再来,具体的方法就是看书上网查,这里就不做详细步骤介绍了.这章就有点意思了,是源码的下载和编译,有能看的,能自己鼓捣的,本章介绍的 ...

  9. Android深度探索-卷1第三章心得体会

    第三章整章介绍了git,git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理.通过配置git后可以很方便的找到需要的资源,更多的是代码和包,可以在本地建立版本库,为了 ...

随机推荐

  1. 剑指offer——58数组中数值和下标相等的元素

    题目三: 数组中数值和下标相等的元素. 假设一个单调递增的数组里的每个元素都是整数并且是唯一的.请编程实现一个函数,找出数组中任意一个数值等于其下标的元素.例如,在数组{-3,-1,1,3,5}中,数 ...

  2. 剑指offer——570~n-1中缺失的数字

    题目:0~n-1中缺失的数字. 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内. 在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字. ...

  3. openssl编译方法

    受不了了,终于编译成功了openssl,写一下编译方法吧 准备: 0:要编译openssl,必不可少的是代码,去下载 https://www.openssl.org/source/ 1:要有一个VS系 ...

  4. Raft——可理解的分布式一致性算法

    Raft  Understandable Distributed Consensus http://thesecretlivesofdata.com/raft/ 一个直观的动画,便于理解raft算法. ...

  5. codeforces 24d Broken robot 期望+高斯消元

    题目传送门 题意:在n*m的网格上,有一个机器人从(x,y)出发,每次等概率的向右.向左.向下走一步或者留在原地,在最左边时不能向右走,最右边时不能像左走.问走到最后一行的期望. 思路:显然倒着算期望 ...

  6. C# Func和匿名方法 简单使用

    今天敲代码遇见一个问题,解决中用到了C#的Func和匿名方法,发现挺好用的 定义一个这样的方法可以避免重复写try catch 这里用到了Func<int,string> ,它其实就是一个 ...

  7. js排他功能示例

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  8. 第二天:PowerShell别名

    1.查询别名: Get-Alias -name ls Get-Alias -name dir Get-Alias -name fl Get-Alias -name ft 2.查看可用的别名 查看可用的 ...

  9. js的三种消息框alert,confirm,prompt

    原文:http://blog.csdn.net/lixiang0522/article/details/7764730 <html> <head> <script typ ...

  10. Servlet(Server Applet) 详解

    Java编写的服务器端程序.其主要功能在于交互式地浏览和修改数据,生成动态Web内容. Servlet的工作模式 客户端发送请求至服务器 服务器启动并调用Servlet,Servlet根据客户端请求生 ...