我正在测试以下代码:
type
TPersonA = class
public
procedure Speak;virtual;
end;
TPersonB = class
public
procedure Speak;virtual;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var PersonA: TPersonA;
PersonB : TPersonB;
begin
PersonA := TPersonA.Create;
PersonB := TPersonB.Create;
PersonA := Pointer( PersonB );
PersonA.Speak;
end;
procedure TPersonA.Speak;
begin
ShowMessage('Hello');
end;
procedure TPersonB.Speak;
begin
ShowMessage('Hello again');
end;
end.
因此,如果我使用虚拟方法运行此代码,并将PersonB的指针传递给PersonA并调用Speak,则会执行PersonB.Speak。
但是,如果我在这两个方法中都删除了虚拟指令,然后再次运行,则delphi将PersonA的方法作为静态方法执行,因为它的地址将被直接编译到调用位置的代码中。
因此,当两个方法都声明为虚拟方法时,我更改了代码:
PersonA:=指针(PersonB)==> PersonA:= @PersonB
我遇到了访问冲突。我想在第一种情况下是在使用指针指针,但是在那种情况下我对@的用法感到困惑。
答案 0 :(得分:6)
类型源自TObject
的变量实际上是指向实例的指针。
所以Pointer(PersonB)
也是实例的指针。但是@ObjectB
是指向实例的指针的地址。那是间接的一个额外层次。
FWIW这两个选项都是没有意义的,不会导致有用的地方。
对于删除virtual
指令时的不同行为,使用实例的运行时类型调度虚拟方法,使用实例变量的编译时类型调度非虚拟方法。代码完全运行的事实是由于两个不相关的类具有兼容的VMT。但这只是实现细节的机会。
答案 1 :(得分:6)
让我们做一个简单的图(组成地址):
Address Value
+-----------------+
12345600 | Obj variable | 45680000
+-----------------+
|
v
+-----------------+
45680000 | instance |
| |
| |
+-----------------+
Obj
是一个变量。它是一个对象引用,这意味着它实际上是指向实例的指针。
如果采用@Obj
,则采用变量的地址。其类型为Pointer
。
因此您得到
@Obj = Pointer($12345600)
。
如果将Obj
强制转换为Pointer
到Pointer(Obj)
,您将获得变量指向的实例地址。它的类型也是Pointer
。
因此您得到
Pointer(Obj) = Pointer($45680000)
。
您甚至可以对此进行测试:
if @Obj = Obj then
Writeln('Same')
else
Writeln('Different');
if Pointer(Obj) = Obj then
Writeln('Same')
else
Writeln('Different');
您应该得到:
Different
Same
我将Object
更改为Obj
,因为在Delphi中object
是保留字。
我在指针上的文章中的更多信息:Addressing pointers。
答案 2 :(得分:0)
Pointer(Object)
是强制类型转换,@Object
是对象的地址。换句话说@Object
<> Pointer(Object)
。使用Pointer(Object)
时,您没有获得指向Object的指针,而获得了原始指针的值。