类的继承---跨过中间类,继承祖先类的方法(80分)

  • 主题发起人 主题发起人 xbl
  • 开始时间 开始时间
Sorry,以前写得有点复杂,这个简单.你只要知道Self指针就指向了虚拟方法表的入口,
Self指针负的偏移量是一些类方法和RTTI信息的地址就行了.
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TA = class(Tobject)
private
FNum1: string;
FNum2: Integer;
FNum5: string;
FNum6: string;
public
procedure fun(X: string
Y: Integer
Z: TDateTime);virtual;
end;

TB = class(TA)
private
FNote1: string;
FNote2: TDateTime;
FNote3: Integer;
protected
X1: Integer;
public

procedure fun(X: string
Y: Integer
Z: TDateTime);override;
end;

TC = class(TB)
private
FNote4: string;
FNote5: TDateTime;
FNote6: Integer

protected
X2: string;
public

procedure fun(X: string
Y: Integer
Z: TDateTime);override;
end;


TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;




var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TA.fun(X: string
Y: Integer
Z: TDateTime);
begin
FNum1 := 'aabb';
showmessage( x + FNum1 + inttostr(y) + floattostr(z));
end;

procedure TB.fun(X: string
Y: Integer
Z: TDateTime);
begin
ShowMessage('B');
end;

procedure TC.fun(X: string
Y: Integer
Z: TDateTime);
var
p:pointer;
begin
p := Pointer(classparent.ClassParent)
//获得祖父类虚拟方法表入口地址;
if integer(p) <> 0 then
ta(@p).Fun(x,y,z)
//取得self指针,强制类型转换.
end;
procedure TForm1.Button1Click(Sender: TObject);
var c1,c2:tc;
begin
c1 := tc.Create;
c1.Fun('1',34,44.3333);
c1.Free;
end;

end.
 
希望指针高手给解释一下。
 
这里有一篇介绍VCL模型的文章.
http://www.c-view.org/journal/003/vcl_chong.htm
不过作者用的是C++ Builder,因为他痛恨PASCAL[:)]
但原理是一模一样的.
 
xeen:
非常谢谢您,后面那种方法我明白了!
这昨天我看了一篇文章:《delphi的原子世界》,里面也谈到了VMT的有关内容,
我对您前面的那种方法很有兴趣,您通过计算Fun在VMT的地址来调用Fun,
也就是 a(integer(p)-76).Fun,我真的很想知道: 如果我把A,B,C的类声明改变一下,
应该如何去计算那个偏移量呢, 您再教教我好吧?

//假设我把类的声明变成这样:
type
TA = class(Tobject)
private
FNum1: string;
FNum2: Integer;
FNum3: Float;
FNum4: DateTime;
FNum5: string;
FNum6: string

public
function temp1(X: string;): string
virtual;
procedure temp2(X: Integer
Y: TDateTime): string
virtual;
procedure fun(X: string
Y: Integer
Z: TDateTime);virtual;
end;

TB = class(TA)
private
FNote1: string;
FNote2: TDateTime;
FNote3: Integer;
protected
X1: Integer;
public
function temp3(S: string): string
virtual;
procedure temp4(I: Integer): string
virtual;
procedure fun(X: string
Y: Integer
Z: TDateTime);override;
end;

TC = class(TB)
private
FNote4: string;
FNote5: TDateTime;
FNote6: Integer

protected
X2: string;
public
function temp5(S: string): string

procedure temp6(S: string)
string;
procedure fun(X: string
Y: Integer
Z: TDateTime);override;
end;

 
to xbl:
解释什么?
 
为什么定义了一个参考类之后就可以了呢?
那是什么原因?
 
Shuzi:
为什么定义了一个参考类之后就可以了呢?
那是什么原因?
 
to xbl:
仔细想想,TC中的Fun方法不要override还是有问题的,那就是父类和祖先类的实例
执行不到TC的代码.要解决这个问题,可能还是得用到VMT.
但是我的第二种方法,就是访问不在同一单元的父类或祖先类的私有变量,定义一个
参考类是可行,在我们的系统中有很多地方使用.
 
Shuzi:
我说也是,你定义一个参考类的做法是怎么想出来的,怎么就可以直接访问在
祖先类中定义的私有变量了呢?我还没有想明白。
 
to xbl:
以下是我们系统中为了扩展Delphi的裁剪板功能的代码,可以说为什么定义参考类就可以
访问祖先类或父类的私有变量:
type
TZRClipBoard=class(TClipBoard) {使程序能够通过裁剪板拷贝基于TBaseObject的对象}
private
procedure Adding;
....
implementation

type
TClipboardRef = class(TPersistent) {为引用裁剪板的私有数据}
private
FOpenRefCount: Integer;
FClipboardWindow: HWND;
FAllocated: Boolean;
FEmptied: Boolean;
end;

procedure TZRClipBoard.Adding;
begin
with TClipboardRef(self) do
if (FOpenRefCount <> 0) and not FEmptied then
begin
Clear;
FEmptied := True;
end;
end;
 
A(Self.ClasseParent.ClasseParent).Fun()?
 
所谓定义参考类和我的指针操作private变量是一个原理。
定义的参考类其实就是为了确定你所关心的变量相对于实例入口的偏移。
和指针操作的唯一区别就是参考类方法是由编译器帮你决定访问的指针偏移量,
我的方法是由你自己计算这个指针偏移量而已。
比如要在TC中访问TA.FNum3的话:
TA = class(Tobject)
private
FNum1: string;
FNum2: Integer;
FNum3: Float;
FNum4: DateTime;
FNum5: string;
FNum6: string

public
function temp1(X: string;): string
virtual;
procedure temp2(X: Integer
Y: TDateTime): string
virtual;
procedure fun(X: string
Y: Integer
Z: TDateTime);virtual;
end;

TB = class(TA)
private
FNote1: string;
FNote2: TDateTime;
FNote3: Integer;
protected
X1: Integer;
public
function temp3(S: string): string
virtual;
procedure temp4(I: Integer): string
virtual;
procedure fun(X: string
Y: Integer
Z: TDateTime);override;
end;

TC = class(TB)
private
FNote4: string;
FNote5: TDateTime;
FNote6: Integer

protected
X2: string;
public
function temp5(S: string): string

procedure temp6(S: string)
string;
procedure fun(X: string
Y: Integer
Z: TDateTime);override;
end;

指针:
type
TRefRecord=record // TB里我们所要跳过的私有变量
FNum1: string;
FNum2: Integer;
FNum3: Float;
end;
PRefRecord=^TRefRecord;

var
pt: PRefRecord;
c: TC;
...
pt := Pointer(Integer(c)+TObject.InstanceSize);
pt^.FNum3 := 5.5;

参考类:
TRef = class
private
FNum1: string;
FNum2: Integer;
FNum3: Float;
end;
var
c: TC;
...
TRef(c).FNum3 := 5.5

这两种方法操作的都是TA.FNum3这个私有变量

同理: 要访问TB.FNote2的话

指针:
type
TRefRecord=record
FNote1: string;
FNote2: TDateTime;
end;
var
pt: ^TRefRecord;
c: TC;
pt := pointer(integer(c)+TA.InstanceSize);
pt^.FNote2 := now;

参考类:
TRef = class(TA)
private
FNote1: string;
FNote2: TDateTime;
end;

var
c: TC;

TRef(c).FNote2 := now;
 
谢谢大家的热心关注!
特别谢谢xeen,Another_eYes,Shuzi......这几天来的耐心指点,
明天来结帖吧!
 
后退
顶部