• 站在对象模型的尖端


    一、Template

      template的三个主要的讨论方向

    1. template的声明,也就是说当你声明一个template class、template class member function等时,会发生什么事情。
    2. 如何”实例化“class object、inline nonmember以及member template functions。这些是”每一个编译单位都会拥有一份实例“的东西。
    3. 如何”实例化“nonmember、member template functions以及static template class members。这些都是每一个可执行文件中只需要一份实例的东西。

      其中实例化表示进程将真正的类型和表达式绑定到template相关形式参数上。

    Template的“实例化”行为

    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 member并不可用,嵌套enum也一样。

      其中,enum Status的真正类型在所有的Point 实例中都一样。但我们依旧只能通过template Point class的某个实例来存取和操作。即我们可以这样写:

    Point<float>::Status s;//正确  
    Point::Status s;//错误  
    
    //同样freeLsit和chunkSize对程序而言也不可用
    Point::freeList;//错误  
    Point::chunkSize;//错误  
    Point<float>::freeList;//正确  
    Point<float>::chunkSize;//正确  
    
    //若是引用
    const Point<float> &ref = 0;
    //此时,编译器会实例化一个Point的float实例,它会被扩展为一下形式:
    //内部扩展  
    Point<float> temp(float(0));  //因为引用并不是无物的代名词,0被视为整数,必须被转化为一下类型的一个对象
    const Point<float> &ref = temp;  
    
    Point<float> 
    //一个class object的定义,不论是由编译器暗中的做(像temp那样),或是由程序员显式的做
    const Point<float> origin;  

      都会导致template class 的实例化。

      然而,member function(成员函数)(至少对那些未被使用过的)不应该被实例化,只有在被使用的时候,C++标准才要求它们被实例化。

      由使用者来主导”实例化“规则主要有两个原因:

    1. 空间和时间效率的考虑。因为一个类可能会有很多member functions,但一个class实例并不一定会用到所有的member function。
    2. 尚未实现的机能。例如:origin的定义需要调用Point的默认构造函数和析构函数,因此只有这两个函数需要被实例化。

      实例化这些函数:目前有两种策略:(1)在编译的时候;(2)在链接的时候。

    Template的错误报告

      在template class中,所有与类型有关的检验,如果牵扯到template参数,都必须延迟到真正的实例化操作发生才进行。

    Template中的名称决议法

    1. scope of the template definition(定义出template的域)
    2. scope of the template instantiation(实例化template的域)
    //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);//书中此处我觉得有问题,因为type的具体值是什么还不知道  
                  //那么如果类型不匹配就无法成功调用foo()函数。还请网友指点一下  
         }  
    private:  
         int _val;  
         type _member;  
    }; 
    
    //scope of template instantiation  
    extern int foo( int );  
    ScopeRules<int> sr0;  

      此时如果有以下调用:

    sr0.invariant();  
    //那么在invariant()中调用的究竟是哪个foo()函数实例?答案是
    extern double foo( double );  

      因为:

      在Template中,对于一个nonmember name(非成员名称)的决议结果,是根据这个name的使用是否  与”用以实例化该template的参数类型“有关而决定的。

    1. 如果不相关,就使用scope of the template definition来决定name;
    2. 如果相关,就使用scope of template instantiation来决定name;

     如果此时有如下调用:

    sr0.type_dependent(); 

      那么此时会调用scope of template instantiation中声明的foo()函数,而在该例子中,共有两个foo()函  数,且此例的_member类型为int,所以调用

    extern int foo( int );  
    //如果是
    ScopeRules< double > sr0;  
    //那么就会调用
    extern double foo( double );

      不管如何演变,都是由“scope of template instantiation”来决定。

      总结如下:

    scope of template definition//用以专注于一般的template class  
    scope of template instantiation//用以专注于特定的实例  

    Member function的实例化行为

      template functions的实例化:

      目前有两个策略,一个是编译时期策略,另一个是链接时期策略。

      但这两个策略都有一个共同的缺点:当template实例被产生出来时,有时候会大量增加编译时间。

    总结

      因为模板类型不确定,所以对一个模板类型的变量赋初值可能会是错误的。因为模板类型不确定,所以并不是所有运算符都会支持。模板最后应该以分号结束。因为,在模板类中,所有关于类型的检查会延迟到实例化之后才会发生。

      一个编译器要保持两个scope contexts,其实也就是模板一般化和模板特化,一个用以一般的模板类,另一个用以专注于特定的实例。

    二、异常处理

      当一个异常发生时,编译系统必须完成以下事情:

    1. 检验发生throw操作的函数
    2. 觉得throw操作是否发生在try区段中。
    3. 若是,编译系统必须把异常类型拿来和每一个catch子句进行比较。
    4. 如果比较吻合,流程控制应该交到catch子句手中。
    5. 如果throw的发生并不在try区段中,或没有一个catch子句吻合,那么系统必须(a)摧毁所有已构造的局部对象,(b)从堆栈中将木目前的函数“unwind”(解除)掉。(c)进行到程序堆栈的下一个函数中去,然后重复上述步骤2~5。

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

      c++的实现中,正常执行时,在每一个函数被推离堆栈之前,函数的 local class objects 的 destructor会被调用。

      如果exception抛出时,exception之前的local object 会析构,后面的代码不执行,所以才需要那些什么auto_ptr什么的包装一层,也就是在前面声明一些auto_ptr,即使遇到exception,auto_ptr 的析构函数也会被调用。

    三、执行期类型识别

    (Type-safe Downcast)保证安全的向下转换操作

      一个保证安全的向下转换操作必须在执行期对指针有所查询,看看它是否指向它所展现的对象的真正类型。因此,要想支持type-safe downcast,在对象空间和执行时间上都需要有一些额外负担:

    1. 需要额外的空间以存储类型信息,通常是一个指针,指向某个类型信息节点。
    2. 需要额外的时间以决定执行期的类型(run type),因为,这需要在执行期才能决定。

      C++的RTTI(运行时类型识别)机制提供了一个安全的downcast设备,但只对那些展现多态(也就是使用继承和动态绑定)的类型有效。

      通过声明一个或多个虚拟函数来区别class声明来分辨出这些类型。

      在C++中,一个具备多态性质的class,正是内含继承而来(或直接声明)的虚拟函数。

      c++ 的 RTTI(Run time type identification)执行期类型识别,有的编译器是在virtual table里的一个指针指向具体的 type_info结构来实现的。

      假如产生一个exVetex异常(exVetex继承自exPoint),下面两种写法有些不同:

    //1.
    catch(exPoint p){
        throw; //继续抛出的是exVetex,p是一个临时对象
    }
    
    //2.
    catch(exPoint &p){
        throw;//继续抛出的是裁剪后的exPoint
    }

      只有在一个catch子句评估完毕并且知道它不会再抛出exception之后,真正的exception object才会被销毁。

    引用不是指针

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

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

      dynamic_cast运算符也适用于引用上,然而对于一个non-type-safe cast,其结果不会与施行于指针的情况  相同。因为引用不可以像指针那样“把自己设为0来表示”no object“”。

      Waring:若将一个引用设为0,会引起一个临时对象被产生出来,且该对象的初值为0。

      因此,当dynamic_cast施行于一个引用时,会发生下列事情:

    1. 如果引用真正参考到适当的继承类,downcast会被执行而程序可以继续进行。
    2. 如果引用并不真正是某一种继承类时,那么,由于不能传回0,因此抛出一个bad_cast 异常。

    typeid运算符

      typeid运算符,主要用于判断两个对象是否是同一个类。返回值为type_info。

      ypeid 返回的是一个class的typeInfo的结构。

  • 相关阅读:
    socket 中文man页面函数
    指针和数组(上)
    char和unsigned char--数据类型区别
    自己的总结
    warning C4305:“初始化”:从“double”到“float”截断
    指针数组和数组指针区别
    Python模块常用的几种安装方式
    Jenkins环境搭建
    wxPython:事件
    wx.ListCtrl简单使用例子
  • 原文地址:https://www.cnblogs.com/tianzeng/p/12177852.html
Copyright © 2020-2023  润新知