深度探索C++对象模型之站在对象模型的尖端

1,Template(模板):

template被用于属性混合(如内存配置策略)或互斥机制(使用于线程同步化控制)的参数化技术之中。它甚至被使用于template metaprograms(模板元编程)技术:class expression templates(类表达式模板)将在编译时期而非执行期被评估,因而带来重大的效率提升。

 

2,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。这些都是每一个可执行文件中只需要一份实例的东西。

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

 

3,有如下template class:

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));
const Point<float> &ref = temp;

 因为引用并不是无物的代名词,0被视为整数,必须被转化为一下类型的一个对象:

Point<float>

  一个class object的定义,不论是由编译器暗中的做(像temp那样),或是由程序员显式的做:

const Point<float> origin;

 都会导致template class 的实例化。

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

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

(1),空间和时间效率的考虑。因为一个类可能会有很多member functions,但一个class实例并不一定会用到所有的member function。

(2),尚未实现的机能。

例如:origin的定义需要调用Point的默认构造函数和析构函数,因此只有这两个函数需要被实例化。

4,什么时候实例化这些函数:

  目前有两种策略:

(1),在编译的时候;(2),在链接的时候。

5,template的错误报告

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

6,Template中的名称决议法

 scope of the template definition(定义出template的域)

 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的参数类型“有关而决定的。

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

 如果相关,就使用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//用以专注于特定的实例

 7,Member function的实例化行为

 template functions的实例化:

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

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

8,异常处理

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

(1),检验发生throw操作的函数

(2),觉得throw操作是否发生在try区段中。

(3),若是,编译系统必须把异常类型拿来和每一个catch子句进行比较。

(4),如果比较吻合,流程控制应该交到catch子句手中。

(5),如果throw的发生并不在try区段中,或没有一个catch子句吻合,那么系统必须(a)摧毁所有已构造的局部对象,(b)从堆栈中将木目前的函数“unwind”(解除)掉。(c)进行到程序堆栈的下一个函数中去,然后重复上述步骤2~5。

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

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

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

 (1)需要额外的空间以存储类型信息,通常是一个指针,指向某个类型信息节点。

 (2)需要额外的时间以决定执行期的类型(run type),因为,这需要在执行期才能决定。

 

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

 而如何分辨出这些类型呢?

 答:通过声明一个活多个虚拟函数来区别class声明。

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

10,引用不是指针:

 程序执行中对一个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 异常。

 

你可能感兴趣的:(C++)