我正在测试以下代码:
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.
Run Code Online (Sandbox Code Playgroud)
因此,如果我使用方法作为虚函数运行此代码并将PersonB的指针传递给PersonA并调用Speak,则执行PersonB.Speak.
但是,如果我在两个方法中删除虚拟指令并再次运行,那么delphi将PersonA的方法作为静态执行,因为它的地址将被直接编译到它被调用的地方的代码中.
因此,当两个方法都声明为虚拟并且我更改了代码时:
PersonA:=指针(PersonB)==> PersonA:= @PersonB
我遇到了访问冲突.我认为在第一种情况下是指针指针,但在这种情况下,我已经混淆了@的用法.
类型来自的变量TObject实际上是指向实例的指针.
所以Pointer(PersonB)也是指向实例的指针.但是@ObjectB指向实例的指针的地址.这是一个额外的间接水平.
FWIW这两个选项都没有意义,并且不会带来任何有用的选择.
对于删除virtual指令时的不同行为,使用实例的运行时类型调度虚方法,使用实例变量的编译时类型调度非虚方法.代码运行的事实是由于两个不相关的类具有兼容的VMT.但这只是实施细节的一个机会.
让我们制作一个简单的图表(地址组成):
Address Value
+-----------------+
12345600 | Obj variable | 45680000
+-----------------+
|
v
+-----------------+
45680000 | instance |
| |
| |
+-----------------+
Run Code Online (Sandbox Code Playgroud)
Obj是一个变量.它是一个对象引用,这意味着它实际上是一个指向实例的指针.
如果你采取@Obj,你采取变量的地址.它的类型是Pointer.
所以你得到了
@Obj = Pointer($12345600).
如果Obj转换Pointer为as 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');
Run Code Online (Sandbox Code Playgroud)
你应该得到:
Different
Same
Run Code Online (Sandbox Code Playgroud)
我换Object到Obj,因为object是保留字,在Delphi中.
关于指针的文章中的更多信息:寻址指针.