[读书笔记] - 《深度探索C++对象模型》第6章 & 第7章

1.全局对象

Matrix identity;

main()
{
    // identity必须在此处被初始化

    Matric m1 = identity;
    ...
    return 0;
}

像identity这样的所谓global object如果有constructor和destructor的话,我们说它需要静态的初始化操作和内存释放操作。

C++程序中所有的global objects都被放置在程序的data segment中。如果明确指定给它一个值,object将以该值为初值。否则object所配置到的内存内容为0。

int v1 = 1024;
int v2;

v1和v2都被配置于程序的data segment,v1值为1024,v2值为0(这个C略有不同,C并不自动设定初值)。在C语言中一个global object只能够被一个常量表达式(可在编译时期求值的那种)设定初值。当然,constructor并不是常量表达式。虽然class object在编译时期可以被放置于data segment中并且内容为0,但constructor一直要到程序激活(startup)时才会实施。必须对一个“放置于program data segment中的object的初始化表达式”做评估(evalute),这正是为什么一个object需要静态初始化的原因。

为每一个需要静态初始化的档案产生一个_sti()函数,内带必要的constructor调用操作或inline expansions;在每一个需要静态的内存释放操作的文件中,产生一个_std()函数,内带必要的destructor调用操作,或是其inline expansions。

2.Template的“具现”行为(Template Instantiation)

template <class Type>
class Point
{
public:
    enum Status { unallocated, normalized };

    Point(Type x = 0.0, Type y = 0.0, Type z = 0.0);
    ~Point();

    void* operator new(size_t);
    void operator delete(void*, size_t);
private:
    static Point<Type>* freeList;
    static int chunkSize;
    Type _x, _y, _z;
};

首先,当编译器看到template class声明时,它会做出什么反应?在实际程序中,什么反应也没有!也就是说,上述的static data members并不可用。nested enum或其他enumerators也一样。

Point<float>::freeList;

Point class的float实体会被具现。

Point<double>::freeList;

Point class的double实体会被具现。

Point<float>* ptr = 0;

Point class不会被具现。因为一个指向class object的指针,本身并不是一个class object,编译器不需要知道与该class有关的任何members的数据或object布局数据。所以将“Point的一个float实体”具现也就没有必要。

const Point<float>& ref = 0;

一个Point的float实体会被具现出来。这个定义的真正语意会被扩展为:

Point<float> temporary( float(0) );
const Point<float>& ref = temporary;

为什么呢?因为reference并不是无物(no object)的代名词。0被视为整数,必须被转换为Point<float>的一个对象。如果没有转换的可能,这个定义就是错误的,会在编译时被挑出来。

所以,一个class object的定义,不论是由编译器暗中地做(上面的temporary),或是由程序员像下面这样明确地做:

const Point<float> origin;

都会导致template class的“具现”,也就是说,float instantiation的真正对象布局会被产生出来。

然而,member functions(至少对于那些未被使用过的)不应该被“实体”化。只有在member functions被使用的时候,C++ Standard才要求它们被“具现”出来。

目前的编译器,面对一个template声明,在它被一组实际参数具现之前,只能施行以有限的错误检查。template中那些与语法无关的错误,程序员可能认为十分明显,编译器却让它通过了。只有在每一个具现操作发生时才做类型检验。

template之中,对于一个nonmember name的决议结果是根据这个name的使用是否与“用以具现出该template的参数类型”有关而决定的。如果其使用互不相关,那么就以“scope of the template declaration”来决定name。如果其使用互有关联,那么就以“scope of the template instatiation”来决定name。

3.下面的class声明:

class PrimitiveObject : public Geometry
{
public:
    virtual ~PrimitiveObject();
    virtual void draw();
    ...
};

如果它被含入15个或45个程序源码中,编译器如何能够确保只有一个virtual table实体被产生出来呢?编译器会把virtual table放在定义了该class的第一个non-inline、nonpure virtual function的文件中。以上面例子而言,编译器会将virtual table产生在储存着virtual destructor的文件之中。

4.异常处理:

欲支持exception handling,编译器的主要工作就是找出catch子句,以处理被丢出来的exception。这多少需要追踪程序堆栈中的每一个函数的当前作用区域(包括追踪函数中的local class objects当时的情况)。同时,编译器必须提供某种查询exception objects的方法,以知道其实际类型(这直接导致某种形式的执行期类型识别,也就是RTTI)。

如果是在Point ocnstructor中发生exception,此时内存已配置完成,那么Point之中任何构造好的合成物或子对象(subobject,也就是一个member class object或base class object)都将自动被解构掉,然后heap内存也会被释放掉。

类型描述器是必要的,因为真正的exception是在执行期被处理,其object必须有自己的类型信息。RTTI正是因为支持Exception Handling而获得的副产品。

当一个exception被丢出时,exception object会被产生出来并通常放置在相同形式的exception数据堆栈中。从throw端传染给catch子句的是exception object的地址、类型描述器(或是一个函数指针,该函数会传回与该exception type有关的类型描述器),以及可能会有的exception object描述器(如果定义的话)。

catch( exPoint p )
{
    // do something
    throw;
}

考虑一个exception object,类型为exVertex,派生自exPoint。这两种类型都吻合,于是catch子句会作用起来。那么p会发生什么事?

1>p将以exception object作为初值,就像是一个函数参数一样。这意味着如果定义有(或由编译器合成出)一个copy constructor和一个destructor的话,它们都会实施于local copy身上。

2>由于p是一个object而不是一个reference,当其内容被拷贝的时候,这个exception object的non-exPoint部分会被切掉(sliced off)。此外,如果为了exception的继承而提供有virtual functions,那么p的vptr会被设为exPoint的virtual table;exception object的vptr不会被拷贝。

5.RTTI和dynamic_cast

程序执行中对一个class指针类型施以dynamic_cast运算符,会获得true或false:

如果传回真正的地址,表示这个object的动态类型被确认了,一些与类型有关的操作现在可以施行于其上;如果传回0,表示没有指向任何object,意味应该以另一种逻辑施行于这个动态类型未确定的object身上。

dynamic_cast运算符也适用于reference身上,但其结果不会与施行于指针的情况相同:

一个reference不可以像指针那样“把自己设为0便代表no object”,若将一个reference设为0,会引起一个临时性对象被产生出来,该临时对象的初值为0,这个reference然后被设定成为该临时对象的一个别名(alias)。

因此当dynamic_cast运算符施行于一个reference时,不能够提供对等于指针情况下的那一组true/false。取而代之的是,会发生下列事情:

1>如果reference真正参考到适当的derived class(包括下一层或下下一层...),downcast会被执行而程序可以继续进行;

2>如果reference并不真正是某一张derived class,那么,由于不能够传回0,于是丢出一个bad_cast exception。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值