跨单元、跨类地访问Delphi类的私有域
来源:互联网 发布:网络歌曲最dj经典500首 编辑:程序博客网 时间:2024/06/14 08:35
======================================================
注:本文源代码点此下载
======================================================
跨单元、跨类地访问delphi类的私有域
作者: aimingoo 发表日期: 2003-8-20 12:51:44 访问次数: 436
主页: http://www.doany.net/
如何跨单元、跨类地访问delphi类的私有域
delphi约定,一个私有方法不能被其它模块中调用,同时,一个私有属性不能在其它模块中读写。但delphi也在同一单元中放宽了上述限制,也就是说,你可以在同一单元的一个类中访问其它类的私有域和私有方法。
但是,有没有办法不在同一单元访问它们呢?
似乎delphi不提供这样的机制,但是,我们的确可以做到!
一、 问题是怎样被提出的?
oop方法总是试图安全、快速地构造一个类,并在此基础上继承出子类。为了保证子类对父类操作,以及类实例操作的安全性,oop中引入了类的私有域的概念。也就是通常说的private域。
如前所述,你可以改写一个类的单元,在该单元中实现对类的私有域的访问。但是,没有人是愿意为每一个delphi单元来改写代码以实现上述要求,更多人总是希望如同访问一个公开域一样直接地访问私有域,而不受限于任何单元。
——这是不安全、不允许的!oop方法的基础理论会这样告诉你。
我们应当承认这样做的“不安全”,但funs们不会接受“不允许”。
问题的提出总是在解决另一个问题的过程之中。——编程历来如此!下面,我将结合一个实现快速删除tlist的一批连续结点时的实例,来描述问题提出和实现的全部过程。它将提出一种全新而有效地跨单元、跨类地访问delphi类的私有域的方法。
按照目前delphi对类的实现机制和方法,这样的一个“后门”还将永远地开下去,funs们就好好的利用吧。哈哈。
*注:下面的实现过程最早被我发布在delphi大富翁论坛(www.delphibbs.com)上。
但在一个长篇累牍的贴子里。
它并没有引起大家的重视。
于是我重新整理了本文。至于原文。
大家不妨到下面的贴子里去看看(建议你仔细而小心地阅览该贴。
否则你会被淹没的……):
http://www.delphibbs.com/delphibbs/dispq.asp?lid=650664
二、 从tlist开始分析……
为了写一个更好的性能isapi filter,我需要更快速地从tlist中删除部分连续的item。比如这样的一段代码:
var p : pchar = 'abcdefgh';
procedure testdelfromtlist;
var t1 : tlist;
i : integer;
maxi : integer;
begin
t1 := tlist.create;
t1.count := 100000;
for i:=0 to t1.count-1 do t1[i] := p;
maxi := t1.count-10-1; //最后一个结点
for i:= maxi downto 10 do t1.delete(i);
//正向删除
//for i:=10 to t2.count-10-1 do t2.delete(10);
end;
这段代码是初始化一个100000个结点的list,然后删除其中的第10个到倒数第10个。这段代码是逆向的,这样的删除速度比较快。如果换成正向删除(已经注释掉),则速度就慢得非常多了。
这样的删除是正常的算法。用测效率的程序测试:逆向删除算法的耗时是21.66个毫秒,则正向删除的耗时却能达到58099.02个毫秒。速度慢了2680倍!!!
但这样就很快了么?不是!我认为就算是逆向删除的速度也并不是快的。
分析tlist这个类的源码,我们可以看到,它是这样写的(我加入了注释):
procedure tlist.delete(index: integer);
var
temp: pointer;
begin
if (index = fcount) then //判定index值是否超界
error(@slistindexerror, index);
temp := items[index]; //取待删除结点
dec(fcount); //count减一
if indexnil then //发通告
notify(temp, lndeleted);
end;
由于在tlist类是将全部的结点指针存放在flist这个动态数组的指针中,所以只需要将index+1之后的内存块向前移4个字节,即sizeof(pointer),即可实现index结点的删除。
但是,如果使用这样来删除成批连续的(n个)结点,则要实现n次system.move()操作,操作的内存块的大小决定了system.move()操作的耗时,而index值越小的的结点在flist中越靠前,则system.move()要操作的内存块也就越大。这就是我认为上述成批删除效率不高的原因,也是正向删除比逆向删除的耗时慢了慢了2680倍的原因。
对于成批删除,理想的算法是从index+len结点开始位置,向前移动count-index-len个结点,这样,就能够一次完成全部的结点移动,实现删除操作。这个思路非常好,至少我认为是这样。为此,我实现了下面的代码:
procedure cutlist(alist:tlist; left,len:integer);
begin
with alist do begin
system.move(list^[left+len], list^[left], (count-left-len) *
sizeof(pointer));
count := count-len;
end;
end;
这段代码的功能是在tlist.list这个buffer中,将删除后的剩余结点直接移动到left这个位置上,从而完成全部的移动操作。
然后,我们再设count := count-len;来使用个数减少,从而完成了成批量的删除。
好的,如果一切正常,算法的速度将大幅度提升!ohhh,美妙的想法!
但是,真的是这样么?我再用效率测试程序来测试了一*,结果是这样的:
1. 测试数据为10万个结点,则逆向删除算法耗时为20.56毫秒,cutlist()函数耗时9.69毫秒;
2. 测试数据为100万个结点,则逆向删除算法耗时为209.13毫秒,cutlist()函数耗时98.01毫秒。
速度比逆向算法提高了一倍,而且应该注意到,cutlist()的耗时仍然随数据量的增大而等比例的增大!!!而从cutlist()函数的实现来看,数据量增大,算法耗时应该只增加极少才对。
要知道,只加快一倍速度的cutlist(),并不是我所想要的!!!但为什么cutlist()函数得不到更高的性能呢???
三、 面对私有域fcount,我咬牙切齿!!!
再次分析tlist类的实现代码,发现count接口实现时,是用setcount来写值,即cutlist()函数中,
count := count-len;
一行的调用,实际上将调用
tlist.setcount(count-len);
而tlist.setcount()的实现代码如下:
procedure tlist.setcount(newcount: integer);
var
i: integer;
begin
if (newcountmaxlistsize) then error(@slistcounterror, newcount);
if newcount > fcapacity then setcapacity(newcount);
if newcount > fcount //如果要增加count的值,则调用fillchar()来填充flist这个buffer
//如果是要减少count的值,则用for循环调用delete()来删除结点
then fillchar(flist^[fcount], (newcount - fcount) * sizeof(pointer), 0)
else for i := fcount - 1 downto newcount do delete(i);
fcount := newcount;
end;
请注意看我在上面注释!oh!事实上,setcount这个操作仍然将调用delete()来删除各个结点(注意,borland为了提高这个删除速度,也使用了逆向删除的算法来实现对delete()的调用)。
所以,我们并不能在cutlist()中得到更好的算法效率!——尽管,我们已经只要,仅仅只需要将fcount设成指定值即可,而并不需要再来一次成批的delete()!
然而,fcount是一个私有域!!!从delphi对oop的实现机制来看,我只能通过setcound()来实现写访问!!!在classes.pas单元中的这段代码清楚地表明了这一切:
tlist = class(tobject)
private
flist: ppointerlist;
fcount: integer;
…
protected
…
procedure setcount(newcount: integer);
…
public
…
property count: integer read fcount write setcount;
…
property list: ppointerlist read flist;
end;
——看到这里,我咬牙切齿!!!
四、 跨域私有域保护!
但是接下来,我注意到一点,我们看到,count读是直接读fcount的值,而写操作是调用setcount()函数。仔细思考一下这个问题:
既然“count读是直接读fcount的值”,那么,count的地址是不是也直接指向fcount呢???
ok,且让我们用一段代码来证明它:
program testfixcount;
uses classes, dialogs, sysutils;
var t : tlist;
pcount : ^integer;
begin
t := tlist.create;
pcount := @t.count; //取得地址?
showmessage(inttostr(pcount^));
t.count := 10000;
showmessage(inttostr(pcount^));
end.
很明显,pcount成功地得到了fcount的地址。哈哈,我们既然已经取得了一个变量在内存中的地址,那么,还有什么是不能做的呢???
我们可以很快完成这样的一段代码来测试对fcount私有域的直接写访问:
program testfixcount;
uses classes, dialogs, sysutils;
var t : tlist;
pcount : ^integer;
begin
t := tlist.create;
pcount := @t.count;
pcount^ := 10;
showmessage(inttostr(t.count));
end.
接下来,我们可以将curlist()函数修改一下下了:
procedure cutlist(alist:tlist; left,len:integer);
var pcount : ^integer;
begin
with alist do begin
system.move(list^[left+len], list^[left], (count-left-len) *
sizeof(pointer));
//count := count-len;
pcount := @count;
pcount^ := count-len;
end;
end;
ok! 我再次用那个可爱的效率测试工具测试了一下下,结果,哈哈,漂亮!——
1. 测试数据为10万个结点,则逆向删除算法耗时仍为20.56毫秒,准确地说是20333.25个微秒,而新cutlist()函数耗时仅为1.08个微秒;
2. 测试数据为100万个结点,则逆向删除算法耗时为212.67毫秒(212668.22微秒),而这种情况下,cutlist()函数耗时仅为1.26个微秒,比10万个结点略略多了一点儿!:)
快了nnnnnnn倍!!!
这才是我要的结果!一个不会随数据量增加而变慢的cutlist()!
五、 突破更多的私有域……
存在的问题是,如果域不提供公用访问接口,那么,也就无法取到它的地址。这种情况下,我们是不是什么也得不到了呢?
仍然参考上述代码上tlist的类定义,我们发现,在private域的定义中,flist和fcount被连续定义,从数据结构的角度来看,通常它们在内存中占用连续的空间。事实上,在delphi中也正是如此处理的。因此,我们只需要得到flist的地址,并用“@list+sizeof(ppointerlist)”,就可以得到fcount的地址了。
我们用下面这段代码来测试不使用tlist.count而直接访问fcount的方法。这意味着,只要我们可以突破一个私有域,我们就可以通过地址计算的方法来突破全部的私有域!
program mytest;
{$apptype console}
uses classes, dialogs, sysutils;
var
t : tlist;
pprivatestrart : pointer;
pcount,pcapacity : ^integer;
begin
t := tlist.create;
pprivatestrart := pointer(@t.list);
pcount := pointer(integer(pprivatestrart) + sizeof(pointer));
pcapacity := pointer(integer(pcount) + sizeof(integer));
t.count := 10000;
writeln('fcount:',t.count);
writeln('fcapacity:',t.capacity);
pcount^ := 10;
pcapacity^ := 1000;
writeln('fcount:',t.count);
writeln('fcapacity:',t.capacity);
end.
六、 其它
1. 由于直接访问私有域的方法超越了delphi的对象保护,所以,访问私有域可能导致一些负面影响。比如,直接修改fcount的方法并不会使flist占用的buffer的空间增大或者减小,也不会使capacity的值发生任何变化。而在delphi对于tlist的类封装中,这三者之间是有联系的。我们有必要进一步地修改cutlist()函数,再加一些代码来维护这种关系,以保证tlist类操作的正常。完整的cutlist()的实现可以在
http://www.delphibbs.com/delphibbs/dispq.asp?lid=650664
或者我的个人主页
http://www.doany.net/ 或http://aiming.ynxx.com/
上找到。
2. cutlist只实现对tlist的连续区间的删除,对于任意item的成批删除,creation-zy在delphibbs上有一段极其精彩的论述。它实现的advsoftdelete()函数可以极其快速地处理任意结点的成批删除。我建议你到
http://www.delphibbs.com/delphibbs/dispq.asp?lid=650664
翻阅一下creation-zy对该问题的论述。
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
- 跨单元、跨类地访问Delphi类的私有域
- 访问类的私有成员
- 访问类的私有成员
- 反射访问类的私有属性对象的私有方法
- 利用Reflection API访问类的私有成员、私有方法
- iOS访问类的私有成员变量及私有方法
- iOS访问类的私有成员变量及私有方法
- 访问私有类成员
- 类的私有数据成员的访问
- 用#define访问类的私有成员
- 利用反射访问类的私有成员
- c#访问类的私有成员
- 指针访问类的私有成员
- 访问基类的私有虚函数
- 访问类中的私有成员的
- 如何直接访问类的私有成员?
- 类访问自身的私有成员
- Delphi的程序单元
- VS2005下的ajax学习(序)
- Delphi用DBExpress连接sybase数据库(原创)
- javascript解析json
- The Expressive Web:最具创意的HTML5和CSS3特性展示
- DELPHI DATASNAP 2010 入门操作(1)为什么要用datasnap 2010
- 跨单元、跨类地访问Delphi类的私有域
- Ajax学习(1)---Ajax 入门简介
- 转载 深入Java核心 探秘Java垃圾回收机制
- DELPHI中宏小用
- HTML5绘制饼图实例(-)
- 关于HTML页以Get方法向asp.net页面传值乱码的解决
- JavaScript绘图
- delphi 游戏外挂封包技术
- Flex的危局,还是HTML5的盛宴?