《Inside the C++ Object Model》笔记(1~7章)

 

第一章 关于对象

C语言中,数据处理数据的操作十分开来的。由一组分布在各个一功能味道想得函数中的算法所驱动,他们处理的是共同的外部数据。而在C++中,则使用的是独立的抽象数据类型abstract data typeADT)”所驱动。从软件工程的角度看一个ADTCLASS hierar5chy的数据封装"C程序中程序迎的使用全局数据好"
加上封装后的布局成本
C++
在布局以及存取时间上的额外负担是由Virtual引起,包括:
Virtual function
机制  用以支持一个有效率的执行期绑定"runtime bingding"
Virtual base class 
用以实现多次出现在继承体系中的baseclass,有一个单一而被共享的实体

1.1  C++ 对象模型 (the c++ object model)
C++中,有两种class data members : static and nonstatic; 三种class member function :static , nonstatic  and virtual
而这些内容的安排方法有:
1
简单对象模型
 
为了尽量减低C++编译器的设计复杂度,配上了空间和执行期的效率。一个object 是一系列的slots,每一个slot之想一个member : data member and function slot. 每个data member and function member都有自己的slot. 此模型中,meember 不放在object中,而是存放slot,slot指向member.
2
表格驱动对象模型
 
此模型中将data member放在一个表中,member function放在一个表中,而class object 本身还有两个表哥的指针.表中的每一个单格存放data member member function
3 C++
对象模型
 
Nonstatic data members 北方旨在没一个class object中,static data members 放在class object 之外,而static and nonstatic function memebers 放在所有的class object 之外。而Virtual functions一两个步骤完成: 1) 每一个class 产生出一对之想virtual functions的指针,放在virtual table  2)每一个class object 添加一个指针,指向相关的virtual table. VPTR的设定和重置是由constructor ,destructor ,copy constructor完成的。而每一个class 索关联的type_infor也是由virtual table完成的,存放在表格的第一个slot中。
 
优点:空间和存取时间的效率;    缺点: 如果应用程序代码本身未曾改变,但是用到的class objectsnonstatic data members有所修改,那么哪些应用程序代码都需要重新编译。

加上继承:
1)简单模型:每一个base class可以被derived class object内的一个slot指出,该slot内涵base class subobject的地址。    缺点:间接性导致了空间和存取时间上的负担, 优点: class object的大小不因base class 而受影响。
2)Base Table
模型。base class table产生出来,表格中的每一个slot内含一个相关的base class地址,很想virtual table内涵一个virtuabl funciton 地址。每个对象含有一个BPTR,指向base class table. 缺点:由于间接性导致了空间和存取时间上的额外负担   优点:每个class object对于继承都有一直的表现方式:每个class object 都应该在一个固定的位置上安放一个base table指针,与base classes的大小和树木无关,而且不改变class objects的本身的大小。
3) C++
的模型: base class subjectdata members直接放在derived class object中,提高了存取的效率,却电视每个base class data member改变一下,derived class 就改变了需要重新编译。而Virtual base class的产生,需要一个间接的base class的表现方法,参看3.4

对象模型如何影响程序:不同的对象模型,会导致现有的程序代码必须修改以及必须加入新的程序代码两种结果。

1.2 关键词所带来的差异
为了支持8种整数的运算,所以C++支持了运算符重载。C++中,必须保证同一个access section内的数据,以其申明的次序出现在内存布局中,而不同的access sections的存放次序是不定的。同样,base class derived class的布局次序也是没有规定的。

1.3  对象的差异
C++
支持的三种programming paradigms:
1)
程序模型:类似于C语言  2)抽象数据类型模型:一组抽象的数据和它的相应表达式 3)面向对象模型:彼此相关的类型,通过一个抽象的base class被封装起来。

C++
一下列的方法支持多态:
1)
经由异族硬汉的转化操作,将derived class object的地址赋给base class pointer  : shapre *ps = new circle();
2)
经由virtual function机制:
ps->ritate();
3)
经由dynamic_case typeid运算符:  if (circle * pc= dynamic_case <circle *> (ps) )   ...

需要多少的内存才能表现一个class object: 1) nonstatic data members的大小  2)alignment的需求 3) Virtual funciton and Virtual base class的额外负担

指针的类型:每个指针的大小是一样的,但是有指向不同类型之各指针间的区别,每个指针只能覆盖其指向对象的内存范围。

 

第二章 构造函数语义学

2.1  Default Constructor 的建构操作
default constructors
在需要的时候才会被编译器产生出来。只有当编译器需要default constructor的时候才会合成出一个constructor, 只有下面的四种情况才会有nontrivial default constructor产生,其余的都是trivial default constructor

1) 带有Default Constructor Member Class Object
如果一个class自己没有定义constructor,但是内含一个member object,而后者含有default constructor,那么次classimplicit default constructor 就是nontrivial,编译器会合成一个constructor .注意被合成出来的constructor 只满足编译器的需要,而不是程序员的需要(只完成他自己向要做的事情)。

如果含有多个class member object ,那么constructor 要求初始化操作按照member object class 中的申明次序调用各个的constructors
2)
带有Default ConstructorBase Class
如果没有定义构造函数,编译器会产生一个nontrivial constructor,先调用相应的base class's constructor . 如果用户自己定义了constructor,那么编译器讲会扩展他,讲义调用所有比要至default constructors的程序代码加进去。

3)
带有一个Virtual Function Class : 因为编译器必须要初始化VPTR VTable
4)
带有一个Virtual Base Class Class : 在此之中,如果向要通过一个指针访问最顶端的classdata member,必须要通过"指向基类的指针"来完成。 pa->_vbcx->i=1024; _vbcx则是在class object的创建的时候完成的。

2.2  Copy Constructor的建构操作
由三种情况使用到Copy Constructor: object作为函数参数传递  函数的返回值是object  直接用一个同类实体最为初值而初始化
如果class没有定义copy constructor,那么编译器可能合成一个copy constructor--memberwise initialization,或者使用bitwise copy

出现不要,bitwise copy的情况和上面的default construcotr的情况一致,
1) class
内含一个member object, 而后者的class具有了一个copy constructor  2)class 继承一个base class,base class 好有一个copy constructor  3) class含有Virtual funciton   4) class白绳子一个继承串链,其中具有Virtual base classes .后面两种比较复杂,因为可能出现derived class base class之间的对象赋值的情况。必须要保证他们的VPTR的正确性。

2.4下列情况中,必须要使用member initialization list进行data member 的初始化:
1)
当初始化一个referecnce member 2)当初始化一个cost member 3)当调用一个base class constructor,而他拥有一组参数的时候 4)调用一个member class constructor ,而他有一组参数
kist中的项目次序是由class中的members声明次序决定的,而不是有initialization list中的排列次序决定。

 

 

第三章 成员data语义学

C++ standard 不强制规定“base class  sunjects的排列次序”“不同存取层记得data members的排列次序,也不规定“virtual funcitons virtual base class“的实现细节

3.1  Data member 的布局
C++ standard
要求:在同一个access section member 按次序排列,而各自之间不一定。 VPTR的存放位置也不一定,个个编译器自行规定。

3.3 Data Member的存取
static data member:
只要是static member,那么存取就是一个固定的时间,因为他相当于一个global变量。如果去一个static data member的地址,就会得到一个指向其数据类型的指针,而不是一个指向其class member的指针。因为static member并不包含在一个class object 之中。既然static member放在global data segment之中,就需要对他进行命名处理,防止冲突啊!
Nonstatic data member :
对一个nonstatic data member进行存取操作,编译器需要把class object的手地址加上data member在对象中的偏移量(offset)origin._x----->  &origin+(&Point3D::x-1) ;  而如果该data member属于一个base class subjects,也一样,因为它的偏移两会在编译期间得到。
虚继承:经由baseclass subject 存取class members”会增加一层新的间接性。如果是由对象访问base class的数据成员,和上面的时间一样,因为它的偏移量在编译期间已经固定了,而如果是有一个指针来访问,则会很复杂,因为不知道该指针是指向的什么类型阿!可能是base class,也可能是一个derived class,析个额外的间接引用,才能解决。

3.4“继承 Data Member
C++
中,一个derived class object的空间是自己的members+base class members! 而之间的排列次序载C++  standard中也为强制指定。
虚拟继承的时候:访问virtual base class的成员,则需要通过一个指向该base class的指针,才能访问,而不是像以前一样直接根据计算得到的偏移量得到。所以虚拟继承的效率很差! 高了一倍耶!

指向Members的指针:

 

第四章 成员funtion语义学

4.1 Member的各种调用方式
   Nonstatic Member Functions :
速度和一般的Nonmember function 有相同的效率。 编译器会对nonstatic member function进行改造。加入this参数,改变函数内部的对member data 的使用方法,改变函数的名字--相当于nonmember function . 这里面有一个函数名字的处理问题,加入class name!
   Virtual Member Funciton :
通过一个object 调用虚拟函数,这时需要通过vptr中转一次,而如果直接通过class::function,则不回缠上不必要的决议操作。而使用classs scope operator明确的调用某个virtual funciton 其决议方式和nonstatic member funciton 一致。

   Static Member Funciton : static member function
的主要特性就是没有this指针,所以他不能够直接存取其classnonstatic data members ; 也不能够被声明为virtual ,const, volatile ; 他因此也就不需要经由某个class object 才能够被调用。他的调用速度和nonmember funciton 差不多。如果取static member function的地址,那么江会是直接“Nonmember函数指针,而不是指向class member funciton的指针

4.2 Virtual Member Function

多重继承下的Virtual Funciton :
当使用base  class pointer 调用virtual function的时候,可能出现两种情况。因为该base class pointer可能指向的位置处于derived class object的开始处,也可能有一个偏移量。一般规则时,经由指向第二或后继之base class”的志真来调用derived class virtual funciton ,改掉用操作所连带的必要的this指针调整操作,必须在执行期完成。

那么怎么处理呢? 可能本来不需要调整的也变得要调整了! 真是讨厌!  嘿嘿!  thunk技术北引用到了编译器中!  怎么解决呢? 
Sun
编译器的方法是提供所谓的“split functions”技术;以相同的算法产生两个函数,其中第二个在返回之前,为指针加上必要的offset,于是不论通过base1或者derived指针调用函数,都不要调整返回值;而如果通过base2指针调用,则是另外一个函数 。

IBM
的方法呢?"thunk搂抱在真正被调用的virtual function中。函数移开实现(1)调整this指针,然后才(2)执行程序员写的函数码;至于不需要调整的函数调用操作,就直接进入(2)部分"

MS
的方法以所谓的'address points'来取代thunk策略,即将用来改写别人的函数(也就是overriding function)期待获得的是'引入virtualclass'的地址。这就是'address point'

4.4  指向Member Function的指针
指向 nonstatic member function 指针 :取一个nonstatic member function 的地址,如果该函数是nonvirtual ,则得到的结果是他在内存中的真正的地址。然而这个值也是不完全的,需要帮盯在某个class object的地址上,才能够通过它调用该函数。所有的nonstatic member functions 都需要对象的地址(以this参数指出)。
指向 static member functions 指针 :而static member functions的指针则是函数指针,而不是指向member function 的指针。
指向 virtual member functions 指针:对一个virtual member funciton 取地址,所能够获得的只是一个索引值。编译器在评估求值的时候,会(*ptr->vptr[(int)pmf])(ptr) !

 

第五章 构造、析构和拷贝语义学

5.2  继承体系下的对象构造
一般而言编译器所做的扩充操作大约如下:
1
、记录在initializataion list 中的data members 初始化裁做会被放进 constructor的函数本身,并以members 的声明顺序为顺序。
2
、如果有一个member 没出现在member initialization list 之中,但是他又一个default constructor ,那么该default constructor 必须被调用
3
、在这之前呢,如果class object 具有virtual table pointers,那么它们必须被设定处置,以指向适当的VTable
4
、在那之前,所有的上一层的base class construcotors必须被调用,以base class 的声明顺序为顺序
 a)
如果base class 被列于member initialization list 之中, 那么任何明确制定的参数都被传递过去
 b)
如果base class 没又被列于member initialization list 之中,而它具有default constructor,那么会调用default construcotr
 c)
如果base class是多重继承下的第二或后继的base class,那么this 指针必须有所调整。
5
、 在那之前,所有的virtual base class constructors 必须被调用,从做到又,从最深到最浅。
 a)
如果 class 被列于member initialization list 之中, 那么任何明确制定的参数都被传递过去, 如果 class 没又被列于member initialization list 之中,而它具有default constructor,那么会调用default construcotr
 b) class
中的每一个virtual base class subject 的便宜量必须在执行期间可被存取
 c)
如果class object是最底层的class, constructors可能被调用,某些用以支持这个行为的机制必须被放进来!
VPTR
的初始化时间:在base class construcotrs 调用操作之后,但是在程序员提供的member initialization list 被调用之前。所以在class member initialization list 中调用该class 的虚拟函数是安全的,但是未必是有意义的。因为其data member 还没有初始化啊!嘿嘿!

虚拟继承: virtual base class 只需要被初始化一次。添加一个辅助变量决定仅仅初始化一次!

 

第六章 Runtime语义学

6.1  对象的构造和解构
对象的解构发生在离开当前区域之前,而不是说大括号之前

对象数组:编译器调用一个宁为vec_new()的函数,产生出以class object对象构造而成的数组。
void * vec_new(void * array,      // 
数组的起始地址
               size_t elem_size,  // 
每个class object的大小

               int elem_cout,     // 
数组元素的个数
               void (* construcotr) (void*) ,  // default constructor
的函数指针
               void (* destructor) (void * , char) // default destructor
的函数指针
参数array带有的若不是具名数组的地址,就是0。如果时0 ,那么数组江经由应用程序的new运算符,被动态配置于heap中! 载vec_new被调用的过程中,会存放相应的cokkie,保存次内存块的元素个数。注意:是元素个数,而不是整个数组的大笑。因此使用base pointer释放 derived pointer会引起内存泄漏噢!
point knowts[10];  --->vec_new(&knots,sizeof(point),10,&point::point,0)
point knowts[10]={ point(),point(1,2,3) } -->point::point (&knots[0] );
                                             point::point (&knots[1] );
                                             vec_new(&&knowts+3*sizeof(point) , sizeof(point), 7,&point::point ,0)

point3d *p_array = new point3d[10];  
         ---->  point3d * p_array;
                p_array = vec_new ( 0, sizeof(point3d),10,&point::point,&point3d::~point3d);
                                         

释放资源:void*  vec_delete(
                  void * array,       // 
数组起始地址

                  size_t  elem_size,  // 
每个object 的大小
                  int   elem_count,   // 
述组中的元素数目

                  void (*destructor) (void*, char)
                 )

Placement Operator new 的语义
point2w *ptw = new (arena) point2w;
释放方法: ptw->~point2w();

第七章 对象模型cusp

7.1 Template

Template具现行为 (template instantiation)
编译器看到template的声明,并不做任何的事情,只有等到template instantiation的时候, 定义出一个template class object 或者 template class refrence(引用必须初始化),才会相应的准备template class 内部的数据排放等等,此时在template class 内部声明的static data member 也才会有内存。而仅仅声明一个template class pointer 并不会有template instantion

注意: 在template class instantiation的时候,其内部的member functions 并不同时跟着被实体化。只有在member functions 被使用的时候, C++ Standard才要求他们被具现出来。原因有二:可能某个程序只使用其中的几个member functions,没有必要为所有的member function 都具现,消耗大量的空间和时间;另外,由于template classs generic programming ,所以某些member function 并不是对任何带入的class都是可用的。所以如果我们同时具现所有的member function,往往可能会造成编译时期的错误,而恰恰程序中根本就不会使用到该不合法member function。 那么,这些member function何时具现呢?有两种策略:1、在编译的时候 ;2、在链接的时候

Template的错误报告
根据刚才所说,member function 由于在程序中被使用的时候才会局限,所以在template class definition的时候不会检查和类型相关的错误,而只是检查语法词汇错误等与类型无关的错误。
Template
中的名称决议方式
C++ Standard中, "scope of the template definition "--- “定义出template"的程序; "scopte of the template instantiation"--- "具现出template"的程序
template之中, 对于一个nonmember name的决议结果是根据这个name的使用是否与用以具现该template的参数类型有关而决定的。如果其使用互不相关,那么使用“scopte of the template declaration"决定其name;如果使用互有关联马么就使用“scope of the template instantiation"来决定name

7.2  异常处理
支持EH,会使那些拥有member class objects 或者base class sunjectsclassesconstructor更复杂。一个class 如果被部分构造,则其destructor必须被是性欲哪些已经被构造的subject 或者 member objects身上。  这一段话并不和More Eff上面的第十款相违背。因为这里的member class objects 是在堆栈中创建的,而More Eff条款十讲的是在堆里面为member class pointer分配内存,如何防止"member class pointer"的未及时删除引起的memory leak

Exception Handling的支持
当一个exception 发生的时候,编译器将会完成一下事情:
1
、检查发生throw操作的函数;
2
、决定throw操作是否发生在try 区段中;

3
、若是,则编译系统必须把exception type 拿来和每一个catch子句比较
4
、如果比较温和,则程序会交到catch这一区段
5
、如果throw 不再try段,或者在try 段,但是没有吻合的catch子句,就会释放active local object,然后移动堆栈指针,到调用者的函数去! 重复2--5

Exception匹配过程中发生的事情可以参考More Eff

7.3  执行期类型识别(RTTI)
千万尽量不要在程序中使用RTTI,参看More EFf
为了支持type_Sage downcast,需要安排一定的空间和时间用于存放和查询某个class object的数据类型。恰好可以和VTable相结合起来。

dynamic_casrt
运算符可以在执行期间决定真真的类型,如果downcast指针是安全的,那么这个运算符会传回被适当转型过的指针;如果不安全,则传回0 。而dynamic_cast相当于((type_infor*)(pt->vptr[0]))->_type_descriptor
dynamic_cast
运算符也适用于reference,但是dynamic_cast失败,不是返回0,而是抛出异常

另外,还可以利用Typeid运算符,typeid运算符传会一个const reference,类型为type_info。而type_info是一个class,可以在VCtypeinfo.h头文件中找到。
程序:
# include <iostream.h>
#include<typeinfo.h>

class B
{
};

void main()
{
 B *pb= new B;

 cout<<typeid(pb).name()<<endl;
}

 

 

posted @ 2007-06-28 10:54  中土  阅读(5164)  评论(0编辑  收藏  举报
©2005-2008 Suprasoft Inc., All right reserved.