博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

VCL第二天

Posted on 2010-12-02 09:29  摸爬滚打的一生  阅读(154)  评论(0)    收藏  举报

类成员包括成员方法和成员数据,成员数据表示类对象的状态,而成员方法是改变类对象状态的操作;

创建一个对象的顺序,先分配对象本身所占用的内存空间,然后执行类的构造函数,初始化数据成员,申请对象需要的资源以及内部的子对象;

销毁一个对象的顺序,先释放对象申请的资源以及内部的子对象,之后是回收对象本身所占的内存空间;

永远不要直接调用Destroy(),而应该是Free();

对象的大小只取决于其拥有的数据成员;

Add('对象大小:' + IntToStr(MyClass1.InstanceSize));
Add('对象所在地址:' + IntToStr(Integer(MyClass1)));
Add('FMember1所在地址:' + IntToStr(Integer(@MyClass1.FMember1)));
Add('FMember2所在地址:' + IntToStr(Integer(@MyClass1.FMember2)));
Add('FMember3所在地址:' + IntToStr(Integer(@MyClass1.FMember3)));
Add('FMember4所在地址:' + IntToStr(Integer(@MyClass1.FMember4)));

对象所在地址(对象的首地址)之后的4个字节存放的是一个指向对象的VMT(虚方法表)的指针;

对象方法可能导致对象的状态发生改变,即对象方法可以更改成员数据的值,所以对象方法可以访问成员数据;

类方法可以改变类的状态,不能改变对象的状态,所以类方法中不能访问成员数据;

类是对实例对象的一个抽象,类之类是对类的一个抽象;

类必须有表示其实例对象的状态的成员数据;

在Object Pascal中,有两个级别的封装,类级和单元级;

类级别的封装,类有4种访问级别,private protected public published,public的成员可以被外界的所有代码直接访问,published和public差不多,区别仅在于published的成员可以被Delphi的开发环境的Object Inspector所显示,因此属性和事件一般声明在此;private的成员,仅类本身和友元可以访问,protected和private差不多,区别在于protected可以被该类的派生类访问;

单元级别的封装包含,在一个Unit中的多个类,互为友元类;在一个Unit的interface部分声明的变量为全局变量,其他Unit可见;
在一个Unit的implementation部分声明的变量为局部变量,其他Unit不可见;每个Unit可有单独的初始化段和反初始化段!

interface部分对外是可见的,声明在interface段中的所有函数,过程,变量的集合,就是单元文件作为一个模块的对外接口,而implementation部分对外是不可见的;

为单元文件提供初始化和反初始化机制,保证单元的独立性,其作用和类的构造函数和析构函数,单元的运作可以脱离对其他模块的依赖;

无论是类的封装还是单元的封装,都是为简化用户接口,隐藏实现细节考虑;

每一个类的实例对象所占用的内存空间,是其自身的成员数据和其所有基类的成员数据所占内存空间的总和;

每一个子类的实例对象,内部都包含了一个完整的基类实例对象,这个对象成为[基类子对象],因此基类的对象永远小于或则等于子类的对象;

虽然子类对象无法访问基类子对象中的private数据,但是,这些数据时的确存在并占用内存空间的;

每一个基类子对象都是完整的;

即使子类对象不能访问基类子对象的private数据,但是他们依然存在内存空间,无法访问时编译器对它做了额外的保护;

继承原则是基类功能弱一些,子类强一些;

多态是通过虚方法实现,而虚方法是通过晚绑定(动态绑定)实现;

封装可以隐藏实现细节,使的代码模块化,继承可以扩展已存在的代码模块,它们都是为了代码重用,而多态为了实现接口重用;

多态的本质就是,将派生类类型的指针赋值给基类类型的指针;

析构函数加上override声明;

当创建一个类的实例之后,编译器会在该实例的内存空间的首4个字节安插1个指针,该指针所指向的地址称为VMT(virtual method table)虚方法表,这个表中存放该类的所有虚方法的入口地址;

一般情况下,几乎每个派生类都要覆盖的方法,将它声明为Virtual,如果只被很少的派生类覆盖,则声明为Dynamic,另外Virtual兼容性更好;

总结:封装,继承,多态是面向对象变成的三个基本特征,实际上,封装只是抽象数据类型(ADT),有了继承才能称为面向对象,而继承的存在,除了扩展现存类的功能外,另一个更重要的作用就是作为多态存在的基石;多态是一种带来灵活性的东西,通过接口重用来实现代码重用;不领会多态,不明白晚绑定,不可能明白什么是面向对象;

健壮的程序来自于正确的错误处理;

VCL(Delphi的应用程序框架)将所有的代码包在一个大的Try Except中,无论什么没有被处理的异常,最终都会被捕获,并将程序流程返回到最外层的消息循环中!异常被VCL捕获,就会出现提示,VCL的异常机制让程序可以继续运行,而不是<非法终止>;

VCL用一个大的Try..Except将所有代码包裹起来,因此在VCL框架中不会有不被处理的异常,也就是说不会有不被处理的错误,异常不等于错误,异常可以被捕获;

从Exception派生自己的异常类;

Delphi高手很大程度上就是VCL高手;

TObject是Object Pascal语言/编译器本身的一个性质,TObject是属于编译器的特性;

TObject封装了Object Pascal类/对象的最基本行为;
TPersistent派生自TObject,TPersistent使得自身及其派生类对象具有自我保存和持久存在的能力;
TComponent派生自TPersistent,这条分支之下的所有的类都成为<组件>,组建的特性:可出现在开发环境的组件板上;能够拥有和管理其他组件;能够存取自身;
TControl派生自TComponent,其分支之下的所有的类,都是运行时可见的组件;
TWinControl派生自TControl,封装了Window系统的屏幕对象,也就是一个真正的Windows窗口(拥有窗口句柄);
TCustomControl派生自TWinControl,组件拥有了Canvas(画布)属性;

对于自定义消息类型,VCL只规定它的首4字节必须是消息编号,其后的数据类型任意;VCL提供了一个TMessage类型用于传递消息,在Object Pascal中,指明类的某个方法为某一特定消息的处理函数,则在其后面添加Message关键字与消息值,比如:
procedure AcceptMsg2000(var msg: TMyMsg); message 2000;

TObject提供了最基本的消息分发和处理的机制,而VCL真正对Windows系统消息的封装则是在TControl中完成;

TControl将消息转换成VCL的事件,以将系统消息融入VCL框架中,那么系统消息如何变成事件;

1:注册窗口类以及窗口函数(window procedure)
2:创建并显示窗口
3:进入主消息循环,从消息队列中获取并分发消息
4:消息被分发后,由Windows操作系统调用窗口函数,由窗口函数对消息进行处理

Application是一个0*0大小的不可见窗口;

VM_打头的为Windows定义的窗口消息,CM_打头的为VCL库自定义的消息,Application.WndProc是不可见窗口的窗口函数,而每个Form都有自己的窗口函数;

所谓组件,就是TComponent的可实例化的派生类,每个组件包括属性,方法和事件;

属性只是一个抽象的名称,它必须有实际的载体,因此必须为每个属性提供一个相应类型的成员数据用来保存属性值,声明在Public段中,就是使Delphi开发环境中的Object Inspector能够识别组件的属性并显示;
可以直接读写某个成员数据,也可以通过成员方法进行读写;
属性覆盖,重新定义属性的可见性,读写途径,其他参数;
属性隐藏,重新定义属性的类型;

所有Public的成员方法都是方法,方法供程序员在运行时调用,而非设计时;

事件是特殊的属性,其类型是一个函数指针,其依附的数据成员必须是函数指针,其实,事件所依附的函数指针类型的数据成员就是用户在组件事件中所编写代码的入口地址,在组件中,要执行用户为事件所编写的代码,只需要调用这个指针即可;
if Assigned(FOnClick) then FOnClick(self);

一般情况下,先考虑TCustomXXX作为自己组件的基类,太多没用的属性会使用户迷惑;