CSDN博客

img evers

关于Delphi下子类调用父类虚函数的一个问题

发表于2004/10/27 9:49:00  4161人阅读

分类: 杂类编程

NO.0 标题:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 15:03:12
两个Delphi class,声明如下: TFather = class public procedure ShowMe; virtual; end; TSon = class(TFather) public procedure ShowMe; override; end; ———————————————————————— 下面是实现 { TFather } procedure TFather.ShowMe; begin Showmessage('Hi , this is father!'); end; { TSon } procedure TSon.ShowMe; begin Showmessage('Hi, this is son'); end; ---------------------------------—————— 现在有这样一段程序: procedure foo; var ASon: TSon; begin ASon := TSon.Create; try {这里如何通过ASon来调用TFather中的ShowMe?} finally ASon.Free; end; end; 我想知道如何在上面程序段中注释部分调用父类TFather的ShowMe方法,C++中只要把变量强制类型转换成父类即可实现,可是我在Delphi中用TFather(ASon).ShowMe,显示的还是"Hi, this is son"。有没有高人告诉我如何通过ASon显示"Hi, this is father"?

[关注本文]   [回复]   [引用]   [返回]   [删除]

NO.1 标题:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 15:08:31
对这个帖子大家都有不少回文,可惜的是数据库坏掉没了.:( 那时候我就觉得C++的这种实现方式是不符合虚拟函数的理念的. 今天我和小白试了类强制转换和类虚拟函数重载,发现两者的实现效果完全一样! #include "stdafx.h" #include "iostream.h" class CF { virtual void showme()=0; }; class CA: public CF { public: int u; void showme(){ cout<<"A:"<u=9; a=b; a->showme(); return 0; } 运行结果: b:9 证实了昨天我的看法:动态绑定函数是不随调用类而改变的, 它对于对象是不变的.

[回复]   [引用]   [返回]   [删除]

NO.2 标题:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 15:10:41
// 再贴一遍我的文章, 希望能和大家一起探讨 我倒不觉得这是一个语言的问题。C++的实现不够OOP,而且我认为它的重载概念也不是很清楚。OOP语言如java、Object Pascal、C#等都有容器、列表等对象,它们不用管理对象到底是什么,只要是对象就行;再如某些算法的实现,如排序,我先定义一个抽象类 TComparableObj = class function CompareObj(Obj: TComparableObj): integer; virtual; abstract; End; (Java里面用接口比较好,Delphi的接口跟com联系太紧密,很不喜欢)然后可以针对这个类作算法实现。我不用管具体比较的是什么,也不用管它们具体是怎么比较的。 这些是老生常谈,大家耳熟能详了。但是如果类型转换后调用的函数是基类的函数实现,那么这些“老生常谈”就无法实现了。我只能为一个具体类复制一套算法函数了。 朱元琪的问题不是语言的问题,而是设计的问题。虚拟函数就是一个申明,声明此类而下将都有这个函数。而具体实现则是每个具体类自己需要管理的事。很多时候子类的实现只是父类实现的延伸,因此Delphi提供了强大的inherited关键字,这给类设计增添了很多方便,同时正如现在所见,给类概念理解造成了一丝困惑。我觉得,虚函数就需要子类完成好针对自己的实现,对于一个对象,居然要调用父类的实现,这只能说是类设计中某个错误的体现。 一个TSon的对象,用as关键词或者用赋值语句或者直接强制类型转换成父类(事实上是可以任意的类),虚拟函数等动态绑定的函数地址是不会变的,简而言之,父类们的实现找不到了。因此我觉得Delphi无法实现朱元琪的愿望了。下面这个例子简单的说明这个问题: TA = class s: string; procedure Showme; end; TB = class s: string; procedure Showme; end; // 实现如下 { TB } procedure TB.Showme; begin ShowMessage('TB: ' + s); end; { TA } procedure TA.Showme; begin ShowMessage('TA: ' + s); end; 下面是一段代码 var a: TA; b: TB; begin a := TA.Create; a.s := 'laugh'; b := TB(a); b.Showme; a.Free; end; 你觉得应该显示什么呢?是'TB: laugh'! 因为showme不是动态绑定的函数,不管对象是什么,用什么类调用就使用什么类的实现。(至于'laugh'为什么能取出来,是因此两个类的内存结构一样,如果把TA定义改一下 TA = class i: integer; s: string; procedure Showme; end; 那么显示的是'TB:',因为TB在内存中应该是string的地方现在是为零的整数,既是空字符串) 下面修改一下类定义 TF = class procedure Showme; virtual; abstract; end; TA = class(TF) s : string; procedure Showme; override; end; TB = class(TF) s: string; procedure Showme; override; end; 同样的代码,这次显示的是 'TA: laugh'! 对于动态绑定的函数,地址是不随调用类而变化的。这时候再把TA改成 TA = class(TF) i: Integer; s : string; procedure Showme; override; end; 程序显示的还是'TA: laugh'!理由同上。 注:上面说的不变、变都是以对象为参照物说的。 文档中把虚拟函数叫做动态绑定是以类为参照物说的。(因为类不知道这个函数的实现到底在哪里) 我理解的很不深刻,而且都是自己的一些见识,没参考什么权威著作,只是平时自己使用类的一些体会,可能很多地方说错了。^_^

[回复]   [引用]   [返回]   [删除]

NO.3 标题:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:苟琦  时间:2002-9-28 15:30:23
" 我想知道如何在上面程序段中注释部分调用父类TFather的ShowMe方法,C++中只要把变量强制类型转换成父类即可实现, " 在C++的里面,这样是实现不了的,正确的是 TSon* pSon = new CSon; pSon->showme() ; //i'm son; ((TFather*)pSon)->showme() ; //i'm son, too; TFather* pFather = pSon; pFather->ShowMe(); //i'm son, another time; pSon->TFather::Showme(); //i'm father!

[回复]   [引用]   [返回]   [删除]

NO.4 标题:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 15:32:36
强! 嘿嘿,我的C++太弱了,根本没想到还能这么调用。 Delphi里面好像没有这样的功能 :(

[回复]   [引用]   [返回]   [删除]

NO.5 标题:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:苟琦  时间:2002-9-28 15:57:50
java里的接口的概念的实现在c++里完全可以用多重继承里的抽象基类来实现。如 IN java: class CFather interface IMother; class CSon public CFather implement IMother; IN c++; class CFather; class CMother { CMother(); virtual ~CMother() = 0; } class CSon : public CFather, public CMother; 因为java设计的目的是创造一种比C++简单,安全的语言,所以去掉了C++里的多重继承,指针,而添加了垃圾处理的机制。这当然丢掉了C++最强大的功能。对于水平中等以下的程序员指针的处理真是一场恶梦,但是对于真正体会了C++妙处的高手来说,那是一个美妙的境界。不过java(还有VB,Delphi)使得这个世界上程序员越来越多,大家可以将编程作为一种娱乐,而在以前却很少人能享受,毕竟不是每个人都可以设计系统程序。 C++里不是没有容器。因为C++是面向机器的设计,所以它已经将程序设计高度抽象,而只提供最基本的功能。标准C++里的STL库就是算法与容器的完美结合,现在还没有任何一种语言的能实现与之能媲美的库,试问,那种语言能够在不知道一组不知道是什么类型的对象情况下对他们进行排序呢?现在最新的jdk1.4里提供了Generic collection library就是为了提供类似的功能。但是效率却差远了,而且我觉得这跟java的设计思想是冲突的,因为现在的java已经越来越复杂了,跟java1.0完全不一样了。

[回复]   [引用]   [返回]   [删除]

NO.6 标题:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-28 17:15:57
to 苟琦: 因为语言的差异,实际上Delphi里的变量实际上就相当于C++里的指针,我在C++里的实现可以看看给小白回的帖子(在VC板块)。 to 施维刚: 我仔细考虑了一下,从道理上讲,这里应该要说到设计的问题。但是,在设计之初,很多东西你是没有办法完全考虑到的,但如果靠Refactoring有时候需要花费很多精力的,而且,如果是别人实现的类,你没法修改他的设计的情况下,你会怎么办?所以,Delphi没有提供相应的实现机制我觉得是一个遗憾。关于你开始提到的容器的问题,只有基本上只有提供模板类的语言才可能实现,比如C++。如果Delphi也要实现容器的话,只能通过单根结构或者接口来实现,但难逃类型安全的宿命。

[回复]   [引用]   [返回]   [删除]

NO.7 标题:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 17:22:50
想起来程序员一篇专门讨论Code Refactoring的文章,我觉得很是感慨。 我们公司现在缺的就是时间和人手来做这些事。可以说,Refactoring在软件开发周期中占很重要的一个比重。

[回复]   [引用]   [返回]   [删除]

NO.8 标题:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-28 17:23:17
写了几个有意思的类,贴上来博一笑: type TMan = Class //人类 public procedure GotoWork; virtual; //上班 end; TSweetGun = Class(TMan) private procedure EatBreakfast;//吃早饭 public procedure GotoWork; override; //上班 end; procedure TSweetGun.GotoWork begin EatBreakfast; inherited; end; 上面是简单的类声明和实现,下面函数中是想说明如果Sweetgun家的锅坏了,他只好不吃早饭就上班了 procedure main var swg: TSweetGun; begin swg := TSweetGun.Create; if 锅没坏了 then swg.GotoWork;//里面调用了吃早饭过程 else if 锅坏了 then {这里如果能提供类似于C++的强制类型转换后调用父类方法的机制就好了,否则,需要更改TSweetGun类的设计,比如在GotoWork前加判断锅坏了没有的语句,由于,设计初SweetGun还没搬家,都在饭店吃的,所以不要考虑锅的问题,但如果要改设计,会设计到许多类的修改,是不是有点累?} end; 其实,在Dephi和VC里做完试验后我就知道Delphi里不能实现了,主要是贴上来引发一下讨论,呵呵。

[回复]   [引用]   [返回]   [删除]

NO.9 标题:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-28 17:27:55
其实,做Refactoring牵涉到一个开发方法的问题,也需要开发人员在设计和编码方面的进一步的提高,所以,我觉得公司应该做一做这些方面的探讨,对大家做一些必要的培训,这样对公司的开发效率和质量都是非常有好处的。

[回复]   [引用]   [返回]   [删除]

NO.10 标题:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 17:30:11
我想这个TSweetGun实现的有问题。^_^ 这种代码是危险的,即TSweetGun吃早饭明明是有条件的,相对而言,去工作时绝对的,可是他确在绝对时间内无条件做了相对事情。 我想TSweetGun应该这样实现: TSweetgun = Class(TMan) private procedure EatBreakfast; // 吃早饭 public procedure GotoWork; override; // 上班 end; 实现如下 procedure TSweetgun.GotoWork; begin if TimeEnough and PosibleToEat then EatBreakfast; inherited GotoWork; end; TimeEnough和PosibleToEat则可以是TSweetGun的成员函数,也可以是全局函数。因为这两者的判断是带有普遍性的。

[回复]   [引用]   [返回]   [删除]

NO.11 标题:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-28 17:33:02
从严格意义上讲,是有危险的,但这里为了表示吃饭和后来凸现的锅的问题之间的关系,把其他的条件给省略了。

[回复]   [引用]   [返回]   [删除]

NO.12 标题:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-28 17:37:25
如上实现后,外面对TSweetGun的使用就没必要管他工作前要做什么了,他只要说 swg.GotoWork。就像公司要我9点上班,这是我的接口,工作前要做什么个人的事,公司就不可涉足,不然难免局面混乱。^_^ 如果碰到这种情况,即TMan这样写了,TSweetGun也这样写了,而我又改不了那么我就这样做 TSweetGunEx = class(TSweeetGun) public procedure GotoWork; override; end; 实现如下 procedure TSweetgunEx.GotoWork; begin if TimeEnough and PosibleToEat then EatBreakfast; GotoWork; end; 如果原来的pas文件都不让我改了,TimeEnough等又是私有域的,那么我只好或参考源码或自己实现,把GotoWork写的更多一些。 呵呵,其实原则就是一个:子类管理好虚拟函数的实现。

[回复]   [引用]   [返回]   [删除]

NO.13 标题:Re:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-28 17:42:36
呵呵,继承一个类也是可以的,不过,这样你就必须要多维护一个仅仅扩充了一个功能的类了,如果在实际操作中带来了过多的维护的话,估计老板会不高兴的^-^

[回复]   [引用]   [返回]   [删除]

NO.14 标题:Re:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:陈绍治  时间:2002-9-28 17:42:51
两位老大,Refactoring是啥意思啊?

[回复]   [引用]   [返回]   [删除]

NO.15 标题:Re:Re:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-29 9:06:16
可以翻译成代码重构 ^_^

[回复]   [引用]   [返回]   [删除]

NO.16 标题:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:施维刚  时间:2002-9-29 9:36:26
《程序员》2001-12期Refactoring有专文讨论,推荐大家一看 //DEVSERVER/王奇飞图书馆/综合/杂志/《程序员》/2001-12.pdf

[回复]   [引用]   [返回]   [删除]

NO.17 标题:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-29 9:37:32
faint,给小白的回帖找不到了,重贴在这里吧(原来在VC版)---------------------------- #include "stdafx.h" #include class CFather { public: virtual void ShowMe(){cout<<"Hi, this is father"<ShowMe(); CFather(*pSon).ShowMe();//强制类型转换 delete pSon; getchar(); return 0; }

[回复]   [引用]   [返回]   [删除]

NO.18 标题:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-29 9:50:55
Refactoring主要用在一些敏捷(Agile)开发方法里面,如XP之类,但对开发人员本身的要求比较高。对于这些方法,我觉得要求比较高,无法大规模的推广。关于这些问题的讨论,如果大家有兴趣,可以单独开个帖子讨论。说实话,虽然和施维刚有意见相左的地方,但是通过讨论可以加深很多认识的:P。其实大家的观点也是不矛盾的,施维刚主要强调完美的设计和对Delphi的积极辩护,而我主要强调从不更改设计(从而不增加开发周期)的前提下,希望Delphi能提供类似于C++的灵活变通,因为没有,所以只能遗憾了。希望大家能对这些方面的问题多作一些探讨,以期共同提高。

[回复]   [引用]   [返回]   [删除]

NO.19 标题:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:苟琦  时间:2002-9-29 9:51:11
“CFather(*pSon).ShowMe();//强制类型转换' 这不是强制类型转换。而是创建了一个CFather对象,对于vc的编译器来说它默认提供两个构造函数,一个是不带参数的,一个就是CFather(CFather&),第二个函数什么都不做,只是将源对象的内存数据全部拷贝一边而已,所以这跟虚拟函数无关。如果你打开vc的调试器会发现pSon的地址与CFather(*pSon)的this指针不一样

[回复]   [引用]   [返回]   [删除]

NO.20 标题:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:朱元琪  时间:2002-9-29 9:59:31
专门搞C++的就是牛啊:)。从实现上看是新构造了一个对象,但在我的印象中,由于和基本类型的强制类型转换的写法是一样的,所以想就是叫强制类型转换的。呵呵,改变了一个错误认识啊。:P

[回复]   [引用]   [返回]   [删除]

NO.21 标题:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:王玮  时间:2002-9-29 10:18:04
呵呵,原来跟delphi中的强制转换还是有差距的看来吃显示器的人的确是牛人啊~~

[回复]   [引用]   [返回]   [删除]

NO.22 标题:Re:Re:Re:Re:Re:朱元琪:关于Delphi下子类调用父类虚函数的一个问题 作者:苟琦  时间:2002-9-29 12:52:10
经过一段hard work,我似乎明白了一点,现在提交给大家看看。发现delphi的虚拟函数表是与C++兼容的,但是跟C++有如下区别: C++里每个类都有自己的虚拟函数表,子类实现的虚拟函数与父类的实现是保存在不同的地址里的,所以C++可以有pChild->CFather::这样的操作苻。但是Delphi的实现不一样,它用子类的实现替换了父类的实现,父类的实现并没有编译进程序里。我是从如下代码里得出的结论,借用上面的TFather,TSon类。 type FunType = procedure; var son : TSon; father : TFather; pFunc : FunType; begin //父类 father:=TFather.Create; pFunc := Pointer(Pointer(integer(Pointer(father)^) + 0)^); pFunc;//调用成功,i'm father pFunc := Pointer(Pointer(integer(Pointer(father)^) + 4)^); pFunc;//调用失败,不是一个函数指针,只有一个虚拟函数 //子类 son := TSon.Create; pFunc := Pointer(Pointer(integer(Pointer(son)^) + 0)^); pFunc;//调用成功, i'm son pFunc := Pointer(Pointer(integer(Pointer(son)^) + 4)^); pFunc;//调用失败,不是一个函数指针,只有一个虚拟函数 end;

阅读全文
0 0

相关文章推荐

img
取 消
img