《深度探索C++对象模型》读书笔记——关于对象【for_wind】

来源:互联网 发布:淘宝推广招聘要求 编辑:程序博客网 时间:2024/06/15 05:35
//整理之,分享之,欢迎指正。for_wind

1、C与C++的区别:

      概括来说,C程序中程序性地使用全局数据[注1]。而C++采用ADT(abstract data tpye)或class hierarchy的数据封装。
      加上封装后,C++在布局以及存取时间上的主要额外负担是由virtual引起的(见本文3、加上继承部分)。
      包括:
(1)virtual function机制(用以支持一个有效率的“执行期绑定”(runtime binding))
(2)virtual base class(用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实体”)。
[注1]:
      在C语言中,“数据”和“处理数据的操作(函数)”是分开来声明的,其语言本身并没有支持“数据和函数”直接的关联性。程序性:由一组“分布在各个以功能为导向的函数中”的算法所驱动,它们处理的是共同的外部数据。

2、C++对象模式

      C++中有两种class data members:static、nostatic,以及三种class member functions:static、nonstatic 和virtual。
  • nonstatic data members被配置于每一个class object之内;
  • static data members则被存在在所有calss object之外,
  • static和 non static function members也被放在所有class object之外。
      virtual function则以两个步骤支持:
(1)virtual table(vtbl):每个class 产生出一堆指向virtual functions的指针,放在该表格内。
(2)vptr:每个class object 被添加一个指向相关virtual table的指针。vptr的设定和重置都由每个class 的constructor、destructor和copy assignment运算符自动完成。每个class所相关的type_info object(用以支持runtime type identification)也经由virtual table被指出来,通常放在表格第一个slot处。

举例:

#include <iostream>using namespace std;class Point{public:     Point (float xval);     virtual ~Point();     float x() const;     static int PointCount();protected:     virtual ostream& print(ostream &os) const;          float _x;     static int _point_count;};

根据上述,下图给出一个基本C++对象模型。

下图给出VS2010中class Point的内存布局[注]:

可以发现,
 (1)float _x;被放在class Point之内。
(2) static int _point_count; Point (float xval); float x() const;static int PointCount();不在class Point之内,即放在所有class object外面。 
(3)virtual table有两项,表示 virtual ~Point();virtual ostream& print(ostream &os) const;
(4)VS编译器添加了指向相关virtual table的指针vfptr放在class object中,位置在data members的前面。
在“Microsoft Visual C++”的编译环境中,我们可以利用编译器“cl”、链接器“link”、可执行文件查看器“dumpbin”来查看Windows下可执行文件(COFF格式)的变量、函数怎么存储。


[注]:
      “cl”即Visual C++ 的编译器,即“Compiler”的缩写。在Visual Studio 2010安装完后,会有一个批处理文件用来建立运行这些工具所需要的环境。它位于开始/程序/Microsoft Visual Studio 2010/Visual Studio Tools/Viusual Studio 2010 Command Prompt,这样我们就可以利用命令行使用VC++的编译器了。
      在“cl”编译器中有个编译选项可以查看C++类的内存布局,使用如下:打开Visual Studio的命令行提示符即Viusual Studio 2010 Command Prompt,cd到文件目录下后,按如下格式输入:
>cl [name.cpp] /d1reportSingleClassLayout[classname]

3、加上继承

(单一继承、多重继承、虚继承[注])
      设想一下两种可能的模型:
(1)简单对象模型,derived class object内的一个slot指出每一个base class (该slot内含base class subobject的地址)。优点是class object的大小不会因base classes的改变而受到影响。缺点是因为间接性而导致空间和存取时间上的额外负担。
(2)base table模型:很像virtual table 内含每一个virtual function的地址,base class table的每一个slot含有一个相关的base class地址。每一个class object内含有一个bptr,被初始化后指向其base class table。 
      不管采用那种模型,“间接性”的级数都将因为继承的深度而增加。

单一继承、多重继承:

      C++最初采用的继承模型并不运用任何间接性:即base class subobject的data members被直接放在derived class object中。这(直接复制模型)提供了对base class 最紧凑而最有效率的存取。缺点是base class members的任何改变后,所有用到此base class或其derived class的objects者必须重新编译。

虚继承:

      那么对于C++2.0新加入的virtual base class呢?
      需要一些间接性的base class表现方法。其原始模型是在class object中为每一个有关联的virtual base class 加上一个指针。其他演化的模型则若不是导入一个virtual base class table,就是扩充原来已经存在的virtual table,以便维护每一个virtual base class的位置。
总结:
      对于单一继承、多重继承采用直接复制模型,对于虚继承则(在直接复制模型的基础上)选择两种间接性模型之一。
      那么VS编译器选择哪种继承模型呢?
      答案是:virtual base class table模型,具体分析见下一篇
注:
      在虚继承的情况下,base class不管在继承串链中被派生多少次,永远只会存在一个实体(subobject)。

4、对象的差异

编程范式:

      C++程序设计模型直接支持三种programming paradigms(编程范式):
(1)程序模型(procedural model):与C一致;
(2)抽象数据类型(abstract data type model,ADT):抽象数据类型是与表示无关的数据类型,是一个数据模型及定义在该模型上的一组运算;其处理的是一个拥有固定而单一的类型的实体,在编译时期就已经完全定义好了。
(3)面向对象模型(object-oriented model):在此模型中有些彼此相关的类型,通过一个抽象的base class(用以提供公共接口)被封装起来。原则上,被指定的object的真实类型在每一个特定执行点之前无法解析。在C++中,只有通过pointer和reference才能够完成。
      在需要多态时,对一个base class object,只有通过pointer或reference的间接处理,才支持OO程序设计所需的多态性质。

C++支持多态的方法

(1)经由一组隐含的转换操作。
(2)经由virtual function机制。
(3)经由dynamic_cast和typeid运算符。

多态的主要用途

      经由一个共同的接口来影响类型的封装,这个接口通常被定义在一个抽象的base class中。这个共享接口是以virtual function机制引发的,它可以在执行期根据object的真正类型解析到底是哪一个函数实体被调用。
     (如何解析呢?通过object的vptr和vptr所指的virtual table。具体见后一篇)

指针的类型:

  • “指向不同类型的各指针”间的差异,既不在其指针表示方法不同,也不再其内容(代表一个地址)不同,而在其所寻址出来的object类型不同。(“指针类型”会教编译器如何解释某个特定地址中的内存内容及其大小)
  • void*类型的指针只能够含有一个地址,而不能通过它操作所指的object。(无类型,不知道其涵盖的地址空间)
  • Pointer或reference之所以支持多态,是因为它们并不引发内存中任何“与类型有关的内存委托操作(type-dependent commitment)”;会受到改变的只是它们所指的内存的“大小和内容解释方式”。


参考资料及推荐资料:
深度探索C++对象模型
《深度探索C++对象模型》侯捷译——笔记(一),读后感,附带【插图】

0 0