C 和Object Pascal对象模型(2)

2008-02-23 05:34:06来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

9.1.3 在基类构造函数调用虚拟方法
虚拟方法在VCL基类构造函数的函数体内被调用,也就是说,类的实现以ObjectPascal模式,分派则以C 模式,取决于对象的运行时类型。因为C Builder综合了ObjectPascal模式的立即配置运行时对象类型,连同C 模式的在派生类构造前构造基类,从VCL风格类的基类构造函数调用虚拟方法会有一些副作用。这些影响在下面描述,并且以至少有一个父类的实例化类为例说明。这里,这个实例化类被作为一个派生类。

1.ObjectPascal模型
在ObjectPascal中,程式员可使用inherited关键字,他提供了一种灵活的方式在一个派生类的构造函数体内任意位置调用基类构造函数。因而,若派生类根据建立的对象重载任何虚拟方法或初始化数据成员,可在基类构造函数和虚拟方法被调用前发生。
2.C 模型
C 语法没有inherited关键字可在派生类的构造函数体内任意位置调用基类构造函数。对于C 模型,使用inherited关键字是不必要的,因为对象的运行时类型是当前被构造类的类型,而不是派生类。因此,虚拟方法的调用是当前的类而不是派生类。因而,在这些方法被调用以前初始化数据成员或建立派生类的对象也是不必要的。

3.C Builder模型
在C Builder中,VCL风格对象的运行时类型,为派生类的类型,并在调用基类构造函数期间不变。因此,假如基类构造函数调用一个虚拟的方法,当派生类重载他时,派生类的方法被调用。假如这个虚拟方法依赖于派生类构造函数体或初始化列表中的任何东西,方法在这发生以前被调用。例如CreateParams是个虚拟的成员函数,他在TWinControl的构造函数中间接地被调用。假如从TWinControl派生一个类并重载CreateParams,以便他依赖于构造函数中的任何东西,在CreateParams被调用以后,这些代码才被处理。这种状况适用于一个基类的任何派生类。考虑一个从B派生的类C,B从A派生。创建C的一个实例,若B重载方法但C没有,A也将调用B重载的方法。

注意 要记住像CreateParams相同的虚拟方法不是被构造函数显式调用的,而是间接被调用。

4.例子:调用虚拟方法
下例比较重载了虚拟方法的C 和VCL风格类。这个例子说明来自基类构造函数的那些虚拟的方法的调用怎么以两种情况被解决。MyBase和MyDerived是标准的C 类。MyVCLBase和MyVCLDerived是从Tobject派生而来的VCL风格类。虚拟方法what_am_I()在派生类中被重载,但
仅在基类构造函数中被调用,派生类构造函数中不调用。


这个例子的输出是:

Iamabase
Iamaderived
这是因为在调用他们各自的基类构造函数期间运行时类型MyDerived和MyVCLDerived的差别。

5.虚拟函数数据成员的构造函数初始化
因为数据成员能够在虚拟的函数被使用,理解他们如何连同何时被初始化是很重要的。在Object Pascal中,任何未初始化的数据被零初始化。这适用于,例如其构造函数没有调用inherited的基类。在标准的C 中,未初始化的数据成员的值不确定。下列类型的类数据成员必须在类的构造函数的初始化列表中初始化:
· 引用。
· 没有缺省构造函数的数据成员。

但是,这些数据成员的值,或那些在构造函数体中被初始化了的数据成员的值,当基类构造函数被调用时,是未定义的。在C Builder中,VCL风格类的内存是零初始化的。注意技术上,VCL类的内存为零,是按位为零,其值实际上是未定义的。例如,一个引用为零。
一个虚拟函数,若依赖于在构造函数体或在初始化列表中初始化的成员变量,可能会表现为似乎变量被初始化到零。这是因为基类构造函数在初始化列表被处理或进入构造函数体前被调用。下例说明这种情况:



这个例子在Base的构造函数中引发一个异常。因为Base在Derived前被构造,not_zero还没被初始化为传递到构造函数的值42。要记住不能在其基类的构造函数被调用前初始化VCL风格类的数据成员。

9.1.4 对象析构
有两种对象析构的机制在ObjectPascal和C 中是不同的。他们是:
· 构造函数中引发异常,析构函数被调用。
· 从析构函数调用虚拟方法。
VCL风格类综合了这两种语言的方法。下面讨论这个问题。
1.从构造函数中发送异常
异常在对象构造期间被发送后调用析构函数的方式在C 和ObjectPascal中是不同的。例如,类C从类B派生,类B从类A派生:



考虑当构造C的一个实例时,异常在类B的构造函数中被引发,在C 、ObjectPascal和VCL风格类中分别会有什么结果,描述如下:

· 在C 中,首先,B的任何已被完全构造了的对象数据成员的析构函数被调用,然后A的析构函数被调用,然后A的任何已被完全构造了的对象数据成员的析构函数被调用。但是,B和C的析构函数不被调用。
· 在ObjectPascal中,仅有实例化的类的析构函数自动被调用。这里是C的析构函数。和构造函数相同,程式员的全部责任就是在析构函数中调用inherited。在这个例子中,假如假定任何的析构函数都调用inherited,那么会按C、B、A的顺序调用他们的析构函数。而且无论inherited在异常发生前是否已在B的构造函数中被调用,A的析构函数都被调用,因为inherited在B的析构函数中被调用。调用A的析构函数单独于他的构造函数是否被实际调用。更重要的,因为通常inherited被立即调用,所以无论C的构造函数体是否完全被执行,他的析构函数都被调用。

· 对于VCL风格类,真正的VCL基类(用ObjectPascal实现)遵循ObjectPascal调用析构函数的方法。C VCL风格类(用C 实现)不严格遵循哪一种语言。在这里,任何的析构函数都被调用;但是那些不是已完成调用的函数体,根据C 语言的规定,不会被进入。从而为以ObjectPascal实现的类提供了一个处理在析构函数体编写的任何清除代码的机会。包括为那些在构造函数异常发生以前被构造的子对象(本身为对象的数据成员)释放内存的代码。需记住,对VCL风格类,清除代码不能被实例化的类或那些C 实现的类处理,甚至析构函数被调用。在C Builder中处理异常的更多信息,参考8.3节。

2.析构函数调用虚拟方法
析构函数分派虚拟方法和在构造函数中模式相同。这意味着对于VCL风格类,派生类首先被销毁,但是在随后任何的基类析构函数的调用中运行时对象类型仍保持为派生类的类型。因此,若虚拟方法在VCL基类析构函数中被调用,可能会分派到已被销毁的一个类。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: c 中函数重载的相关知识

下一篇: C 的算符重载