0%

第七章 站在对象模型的尖端

  • 有三个著名的C++语言扩充性质,它们都会影响C++对象。分别是templateexception handing(EHruntime type identification(RTTI(RTTI可以想象成EH的一个副作用)。

7.1 Template

  • template原本被视为是对container classes的支持,但现在成为STL的基础。它也被用于属性混合(如内存分配机制)或互斥(mutual exclusion)机制(使用于线程同步化控制)。它设置被用于template metaprograms技术:class expression templates将在编译时期而非执行期被评估(evaluated),因而带来效率提升。
Template的“实例化”行为(Template Instantiation)
  • 下面的template Point class:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 Piont<Type> *freeList;
    static int chunkSize;
    Type _x, _y, _z;
    };
    实际程序中,static data members并不可用,nested enum或enumerators也一样。它们每个只能通过template Point class的某个实例来存取或操作
    1
    2
    3
    4
    // ok
    Point<float>::Status s;
    // error
    Point::Status s;
    如果定义一个指针,指向特定实例:
    1
    Point<float> *ptr = 0;
    什么也没发生。因为指向class object的指针,本身并不是class object,编译器不需要直到与该class有关的任何members数据或object布局数据。如果不是pointer而是reference,又如何:
    1
    2
    3
    4
    const Point<float> &ref = 0;
    // 内部扩展
    Point<float> temporary(float(0));
    const Point<float> &ref = temporary;
    所以一个class object的定义,不论是由编译器暗中地做或是程序员显式地做,都会导致template class的实例化。也就是Point中的三个nonstatic members都会被绑定。然而member functions(未被使用过的)不应该被实例化。只有在需要时才实例化。但编译器不遵循这项要求,所以由使用者来主导实例化instantiation),主要有两个原因:
    1. 空间和时间效率的考虑
    2. 尚未实现的机能
Template的错误报告(Error Reporting within a Template)
Template中的名称决议法(Name Resolution within a Template)
  • template的两种意义:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // scope of the template definition
    extern double foo(double);
    template<class type>
    class ScopeRules
    {
    public:
    void invariant() {
    _member = foo(_val);
    }
    type type_dependent() {
    return foo(_member);
    }
    // ...
    private:
    int _val;
    type _member;
    };
    // scope of the template declareation
    extern double foo(double);
    // scope of the template instantiation
    extern int foo(int);
    // ....
    ScopeFules<int> sr0;
    Template中,一个nonmember name的决议结果,是根据name的使用是否与实例化该template的参数类型有关而决定的。如果无关,就以scope of the template declareation决定name,否则以scope of the template instantiation决定name。
    ScopeFules template中有两个foo()调用操作。它会调用foo(double)的版本,此外,_val的类型是int。因为函数的决议结果只和函数的原型(signature)有关,和函数的返回值没有关系。因此_member的类型不会影响哪一个foo()实例被选中。在scope中,只有一个foo()候选者。调用操作由scope of the template declareation决议。如果按以下方式:
    1
    sr0.type_dependent();
    它由scope of the template instantiation决议。它会调用int版本,被type把持。
    这意味着编译器必须保持两个scope contexts:
    1. “scope of the template “,用于专注一般的template class
    2. “scope of the template instantiation”,用于专注特定的实例
Member Function的实例化行为(Member Function Instantiation)
  • 对于template function的实例化(instantiation)。编译器提供了两个策略:一个是编译时期策略,程序代码在program text file中备妥可用;另一个是链接时期策略
  • 如果virtual function被实例化(instantiated),其实例化点紧跟在class的实例化点后。
  • template instantiation似乎拒绝全面自动化,虽然工作做对了,但产生出来的object files重新编译成本可能太高,以手动方式在个别object module中完成预先实例化操作(pre-instantiation)是唯一有效率的方法。

7.2 异常处理(Exception Handing)

  • 想要支持exception handing,编译器的主要工作就是找出catch子句,以处理被抛出的exception。这需要追踪程序堆栈中的每个函数的目前作用区域,同时编译器需要提供某种查询exception objects的方法,以直到实际类型(RTTI)。还需要某种机制管理被抛出的object,包括产生、存储、析构、清理和一般存取。
Exception Handing快速检阅
  • exception handing由三个主要组件构成:throw子句、catch子句和try区段。当一个exception被抛出去,控制权会从函数调用中释放出来,并寻找吻合的catch子句。如果没有,则调用terminate()中断例程。当控制权被放弃后,堆栈中每个函数调用也被推理(popped up)。
对Exception Handing的支持
  • 当exception发生时,编译系统完成以下事情:
    1. 检验发生throw操作的函数。
    2. 决定throw操作是否在try区段中。
    3. 把exception type拿来和每个catch子句比较。
    4. 流程控制交到吻合的匹配的catch子句手中。
    5. 如果throw不发生在try区段中,或没有catch子句吻合。那么:
      1. 摧毁active local objects。
      2. 堆栈中将函数unwind掉
      3. 进行堆栈的下一个函数,然后重复2·5。
决定throw是否发生在一个try区段中
  • 一个函数可以想象为好几个区域:
    • try区段以外的区域,没有active local objects。
    • try区段以外的区域,但有一个(或以上)的active local objects需要析构。
    • try区段以内的区域。
将exception的类型和每一个catch子句的类型做比较
  • 对于每个被抛出来的exception,编译器产生一个类型描述器,对exception的类型进行编码。如果那是derived type,编码内容包含所有base class的类型信息。
  • 类型描述器(type derscriptor)是必要的。因为exception是在执行期被处理的,object必须有自己的类型信息。RTTI正是因为支持EH而获得的副产品。
当一个实际对象在程序执行时被抛出,会发生什么事?

7.3 执行期类型识别(Runtime Type Identification,RTTI)

Type-Safe Downcast(保证安全的向下转换操作)
Type-Safe Dynamic Cast(保证安全的动态转换)
Reference并不是Pointers
Typeid运算符

7.4 效率有了,弹性呢?

动态共享函数库(Dynamic Shared Libraries)
共享内存(Shared Memory)