第9章 VCL的C++语言支持 C++Builder的快速应用程序开发(RAD)能力是建立在用ObjectPascal编写的可视组件库(VCL)基础之上的。本章说明ObjectPascal语言的特征、结构及概念如何在C++Builder中实现以支持VCL。
本章的第一部分比较C++和ObjectPascal的对象模型,并说明C++Builder如何组合这两种方法。本章的第二部分说明ObjectPascal语言的结构如何对应转换成C++Builder中的C++结构。包括一些被增加来支持VCL的关键字的扩展的相关细节。这些扩展,如闭合和属性,是独立的支持基于VCL的代码的有用特征。
注意 从TObject派生的C++类是指那些TObject是其最初的、然而并非直接的祖先的类。为保持编译器逻辑上的一致性,这样的类也被称为“VCL风格类”
9.1 C++和ObjectPascal对象模型 C++和ObjectPascal在创建、初始化、引用、拷贝和销毁对象的方法上有些微小的差异。在本节中描述这些细微的差别和它们对C++BuilderVCL风格类的影响。
9.1.1对象本身和实例 在C++中,类的一个实例是一个实际的对象。那个对象能直接被操作,或者通过引用或指针间接访问它。例如,给定一个构造函数中没有参数的C++类CPP_class,下列变量都是该类的有效实例变量:
相反,在ObjectPascal中,一个Object类型的变量总是间接地引用对象。所有对象的内存都动态地被分配。例如,给定ObjectPascal类OP_class:
ref是OP_class类型的对象的一个“引用”。转换到C++Builder代码,将是:
1.区分C++和ObjectPascal的引用 文档中经常会把ObjectPascal类实例变量作为引用提及,但却作为指针来描述它的行为。这是因为它兼有这两者的属性。一个ObjectPascal引用类似C++指针,但有下列的不同: · 一个ObjectPascal引用是隐式的间接引用(在此情况下更像一个C++引用)。 · 一个ObjectPascal引用不像定义的操作那样有指针运算。 ObjectPascal引用和C++引用比较时,也有类似和不同之处。两种语言的引用都是隐式的间接引用,然而, · 一个ObjectPascal引用可以被重指,而C++引用不能。 · 一个ObjectPascal引用可以是nil,而C++引用必须指向有效的对象。 有些VCL结构下的设计计划是建立在使用这种类型的实例变量的基础上的。指针是最接近Object Pascal引用的C++语言结构。因此,几乎所有的VCL对象的标识符在C++Builder中都被转换为C++指针。
注意ObjectPascal的var参数类型最接近C++的引用。参见9.2.3节的“Var参数”可获得更多信息。
2.拷贝对象 与C++不同,ObjectPascal没有可支持拷贝对象的内嵌编译器。本节描述这个差别对VCL风格类赋值操作符和拷贝构造函数的影响。
(1)赋值操作符 ObjectPascal赋值操作符(:=)不是一个类赋值操作符(operator=())。操作符赋值拷贝引用,而不是对象。在下列代码中,B和C都指向同一个对象:
这个例子在C++Builder中转换为下列代码:
C++Builder的VCL风格类的赋值操作符遵循ObjectPascal语言的规定。这意味着,下列代码中,在两个dereferenced指针之间的赋值是无效的,因为它们试图拷贝对象而不是指针:
注意 对于VCL风格类,使用C++语法的引用是有效的。例如,下列代码是有效的:
尽管与使用赋值操作符不同,但对于澄清和比较而言,这里提出的语法已经足够类似了。
(2)拷贝构造函数 ObjectPascal没有内嵌的拷贝构造函数。因而,在C++Builder中的VCL风格类也没有内嵌的拷贝构造函数。下例中的代码试图使用一个拷贝构造函数创建TButton指针:
对于VCL类,不能编写依赖于一个内嵌的拷贝构造函数的代码。要在C++Builder中创建VCL风格类对象的一个拷贝,可编写一个成员函数以拷贝对象。另外,VCLTPersistent类的派生类可重载Assign方法以从一个对象拷贝数据到另一个对象。例如,对于包含资源图像的图形类,诸如TBitmap和TIcon通常就会这么做。拷贝一个对象的方式最终由程序员(组件开发者)决定;但需注意一些标准C++使用的拷贝方法对VCL风格类是不适用的。
3.对象作为函数参数 正如前面所讨论的,C++和Object Pascal中的实例变量不是等同的。在将对象作为函数的参数传递时应该记住这一点。在C++中,对象可以通过值、引用或指针传递给函数。在Object Pascal中,当一个对象按值传递给函数时,应记住这个对象参数已经是一个对象的引用了。所以,实际上是引用被按值传递,而不是实际的对象。在Object Pascal中,不能像在C++中一样将实际对象按值传递。VCL风格类对象在传 递给函数时遵循ObjectPascal的规定。
9.1.2C++Builder中VCL类的对象构造 C++和Object Pascal构造对象是不同的。本节概述这一问题并说明C++Builder如何综合这两种方式。
1.C++对象构造 在标准的C++中,构造的顺序先是虚基类,然后是基类,最后是派生类。C++语法使用构造函数初始化列表调用基类构造函数。对象的运行时类型为当前构造函数被调用的类的类型。虚拟方法的分派遵循对象的运行时类型并因此在构造期间变化。 2.Object Pascal对象构造 在Object Pascal中,仅有实例化的类的构造函数确保被调用,然而,仍为基类分配内存。直接基类的构造通过在相应的派生类的构造函数中调用inherited来进行。习惯上,VCL类使用inherited调用(非空)基类构造函数。不过,这并不是语言的要求。对象的运行时类型立即被建立为实例化的类的类型,并且不在基类构造函数被调用时变化。虚拟方法的分派遵循对象的运行时类型并且不在构造期间变化。
3.C++Builder对象构造 VCL风格对象的构造与ObjectPascal对象类似,但适用C++语法。这意味着调用基类构造函数时遵循C++语法,对于所有的非VCL的基类和最直接的VCL父类使用初始化列表。这个VCL基类最先被构造。它使用inherited构造自己的基类,遵循ObjectPascal的方法。因此,VCL基类构造的顺序与C++的相反。然后从最远的祖先到派生类,所有的C++基类被构造。对象的运行时类型和虚拟方法分派也基于ObjectPascal。 图9-1说明了一个VCL风格类实例的构造,MyDerived从MyBase派生,MyBase是TWinControl的一个直接的派生类。MyDerived和MyBase以C++实现。TWinControl是以ObjectPascal实现的VCL类。
注意 到对一个C++程序员来说,构造的顺序似乎是反的,因为对于实际的VCL类,它从最边缘的父类开始一直到TObject,然后构造MyBase,最后构造派生类。 注意 TComponent不调用inherited,因为TPersistent没有构造函数。TObject有一个空的构造函数,它也不被调用。若这些类构造函数被调用,其顺序将遵循图9-1中的顺序(这些类为图中带灰影的部分)。 表9-1总结了C++、ObjectPascal、C++Builder的对象构造模型:
<  
1/2 1 2 下一页 尾页 |