DELPHI高手突破读书笔记--类/对象篇

---------------------------------------------------

那什么样的开发工具才是优秀的、体贴的、以人为本的?我的标准是符合以下四点:
(1)能够将要解决的问题简化,并以某种理念快速实现之。
(2)不隐藏任何用户想知道的细节。
(3)可以忽略用户所不想知道的细节。
(4)主动去适应不同层次的程序员。

---------------------------------------------------

永远不要直接调用对象的Destroy(),而应该是Free()。

---------------------------------------------------

类是一道带门的墙,它保护一些东西、隐藏一些东西,同时对外开放一些东西。不能将墙内的所有东西都暴露出来,也不能关闭大门,封闭所有。隐藏实现细节,提供明晰接口是类的第一要务。

---------------------------------------------------

在定义类时,将所有数据成员都声明为public。但实际程序运行结果(指每个数据成员所在地址)并不依赖于它是处于public的还是private的。也就如同上面所说的,即使派生类对象无法访问基类子对象中的private的数据,它们依然是存在并占用内存空间的,无法访问它只是因为编译器为它做了额外的保护。

---------------------------------------------------
覆盖和重载

覆盖是指派生类重新定义基类的虚方法的方法。而重载,是指允许存在多个同名函数,这些函数的参数表不同(或许是参数个数不同,或许是参数类型不同,或许两者都不同)。重载的概念并不属于“面向对象编程”。重载的可能的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说)。例如,有两个重载的同名函数

function func(p : integer) : integer; overload;
function func(p : string) : integer; overload;

那么编译器做过修饰后的函数名可能是:int_func、str_func。如果调用

func(2);
func(′hello′);

那么编译器会把这两行代码分别转换成:

int_func(2);
str_func(′hello′);

这两个函数的调用入口地址在编译期间就已经静态(记住:是静态!)确定了。这样的确定函数调用入口地址的方法称为早绑定。

而覆盖则是:当派生类重定义了基类的虚方法后,由于重定义的派生类的方法地址无法给出,其调用地址在编译期间便无法确定,故基类指针必须根据赋给它的不同的派生类指针,在运行期动态地(记住:是动态!)调用属于派生类的虚方法。这样的确定函数调用地址的方法称为晚绑定。引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态”。

---------------------------------------------------

多态的本质就是“将派生类类型的指针赋值给基类类型的指针”(在Object Pascal中是引用),只要这样的赋值发生了,就是在应用多态了,因为在此实行了“向上映射”(“上下”是指类继承层次关系)。

---------------------------------------------------

TObject类有一个虚拟的Destroy析构函数和一个非虚拟的Free方法。Free方法中首先判断对象本身是否为nil,保证不为nil时便调用Destroy。对任何对象(都是TObject的派生类对象)调用其Free();方法,但执行的都是TObject.Free();(因为TObject.Free()为非虚拟方法,无法被覆盖),然后由它调用被每个类重定义了的析构函数Destroy();(因为Destroy()为虚方法,派生类可以覆盖),这就保证了任何类型的对象都可以正确、安全地被析构。

---------------------------------------------------

被派生类覆盖的方法,则会将派生类实现的方法的入口地址填入VMT中以取代基类被覆盖的方法。派生类的虚方法表完全继承了基类的虚方法表,只是将被覆盖了的虚方法的地址改变了。

---------------------------------------------------

由于DMT中不会出现没有被派生类覆盖的基类dynamic方法,因此DMT会比VMT节省空间(大多数情况下)。当基类有许多虚方法,而派生类只覆盖很少几个时,区别尤其明显。当派生层次越来越深,派生类数量越来越多,DMT就能节省更多的内存空间。但是DMT中对基类的动态方法的寻址不是直接进行的,因此dynamic方法的寻址比virtual方法要慢许多。

virtual和dynamic的区别仅在于编译器采用不同的晚绑定策略而已,对于程序员来说,它们的功能相同。

如何取舍就看实际的需求了,一般情况下,几乎每个派生类都要覆盖的方法,将它声明为virtual;如果类层次很深,或派生类很多,但某个方法只被很少的派生类覆盖,则将它声明为dynamic。
另外需要注意的是,只有VMT才与C++、COM的vtable兼容,因此当需要这样的兼容性时,只能使用virtual。

标签:
文章分类 FK Coding

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

点赞
如果您觉得很赞,我将非常乐意接受虚拟币的捐赠,以示您对我的肯定。

比特币钱包地址:
1PqpqA8FyH3NbfCrbcRd1YxQk3LEsSEYDV
莱特币钱包地址:
LRTdmovGGVEHCKWz7JdL9aiB7VZkuNycJf
站点勋章
网站统计