关于对象的概念性问题

dead_lee 2001-12-05 06:01:19
首先看看以下程序,
program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils;

type
THuman = class // 人类
procedure ShowInf; virtual;
end;

TStudent = class(THuman) // 学生类
procedure ShowInf;override;
end;

TWorker = class(THuman) // 工人类
procedure ShowInf;override;
procedure ShowInfo;
end;

procedure THuman.ShowInf;
begin
Writeln('调用THuman.ShowInf');
end;

procedure TStudent.ShowInf;
begin
Writeln('调用TStudent.ShowInf');
end;

procedure TWorker.ShowInf;
begin
Writeln('调用TWorker.ShowInf');
end;

procedure TWorker.ShowInfo;
begin
Writeln('调用TWorker.ShowInfo');
end;

var
H1 : THuman;
S1 : TStudent;
begin
H1 := THuman.Create;
H1.ShowInf;
H1.Destroy;

H1 := TStudent.Create;
H1.ShowInf;
TWorker(H1).ShowInfo; //这句居然真的调用了TWorker的ShowInfo方法
H1.Destroy;

H1 := TWorker.Create;
H1.ShowInf;
THuman(H1).ShowInf;
H1.Destroy;

S1 := TStudent.Create;
S1.ShowInf;
TWorker(S1).ShowInf;
S1.Destroy;
end.

本来是和别人讨论动态绑定的时候写的一个例子,但是程序中加注释的这句的表现让我大吃一惊,不同的子类之间居然可以合法的强制类型转换而不在运行时出错,而且还堂而皇之的调用了其他子类的方法,Why。
...全文
153 12 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
dead_lee 2001-12-06
  • 打赏
  • 举报
回复
关键在于Hi确实是一个对象的应用,但是该对象中本来是没有TWorker的方法,而且如果说通过强制类型转换就可以得到不属于自己的静态编译接口调用,这个不是太荒谬了吗。
我在java里面测试的话,类似的动作是会有运行时错误的。
xzgyb 2001-12-06
  • 打赏
  • 举报
回复
我想是这样的对于TWorker.ShowInfo是一静态方法,不是c++的那个,前一阵看<<深度探索c++对象模型>>了一点,有一点懂,说的是对于TWoker.ShowInfo这个在内存中就被名子分解为一个独一无二的函数,假如就叫TWOERR_SHOWINFO,函数原型就为TWORKER_SHOWINFO(Self: TWorker)
当TWorker(H1)时,H1本身就是一个地址,转换为TWorker类型,相当于调用TWORKER_SHOWINFO(TWorker(H1));当然可以调用,当如果TWorker中有一些数据成员的话,并且
调用ShowInfo访问了这些成员,就会访问到错误的值,因为在此时Self指向的是TStudent空间

以上个人理解,说错莫怪
xzgyb 2001-12-06
  • 打赏
  • 举报
回复
这么快就结拉,还没讨论够阿
呵呵
dead_lee 2001-12-06
  • 打赏
  • 举报
回复
你的解释现在看来有些道理,好了,结贴.
  • 打赏
  • 举报
回复
问题在于你把三个类定义在一单元中,如果你定义在不同的单元中再试一试。
xzgyb 2001-12-06
  • 打赏
  • 举报
回复
是有些荒谬,java我不懂,可能java里有运行期类型检测吧
如果用H1 as TWorker则肯定会出错,
其实这本身也没有什么荒不荒谬的
类中的函数本身被名字分裂为一个函数,相当于一静态函数调用
不要把这些函数想象为占据该类的内存快内,一个对象所占有的内存空间就是
它所拥有的VPTR和一些数据成员
无论是对于调用TWoker对象的ShowInfo还是Student对象转为TWorker类型的
ShowInfo,汇编码都是如此
mov eax,ebx
call TWorker.ShowInfo

eax即为对象的地址,也即为Self值
所以ShowInfo输出ClassName时
对于TWorker(H1)而言为TStudent
对于真正的TWorker,就为TWorker

这是我的理解,继续讨论,呵呵
dead_lee 2001-12-05
  • 打赏
  • 举报
回复
输出的显示表明父类还是父类,ClassName还是TStudent,但是为什么TStrudent还是可以调用到不属于它的方法呢,我主要的迷惑就是在这里.
这个例子本来写出来是和别人讲解多态的,但是这个结果实在是让我不懂.
dead_lee 2001-12-05
  • 打赏
  • 举报
回复
to xzgyb(老达摩) 
能说得明白点吗
initora 2001-12-05
  • 打赏
  • 举报
回复
TWorker(H1) 呵呵!
xzgyb 2001-12-05
  • 打赏
  • 举报
回复
看产生的汇编吗可以看到对于H1.ShowInf是这样的
mov eax,ebx //ebx为该对象的地址
mov edx,[eax]
call dword ptr [edx]

而对于TWorker(H1).ShowInfo
mov eax,ebx
call TWorker.ShowInfo

所以就是这样的
可以在TWorker.ShowInfo这样
Writeln('调用TWorker.ShowInfo' + '(' + self.ClassName +')');
就可以看出点意思了
gxgxfish0813 2001-12-05
  • 打赏
  • 举报
回复
好像不能影响,因为已经创建完了
王集鹄 2001-12-05
  • 打赏
  • 举报
回复
父大类不能影响子类

5,928

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧