I was testing this following code:
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.
So if i run this code with methods as virtual and pass a pointer of PersonB to PersonA and call the Speak, PersonB.Speak is executed.
However, if i delete the virtual directive in both methods and run again then delphi executes the method of PersonA as static, cause it's adress will be compiled directly into the code at the place it was called.
So, when both methods were declared as virtual and i changed the code:
PersonA := Pointer( PersonB ) ==> PersonA := @PersonB
I got into a access violation. I think was doing in the first case a pointer of pointer but i've gotten confuse with the usage of @ in that case.
A variable whose type descends from TObject
is actually a pointer to the instance.
So Pointer(PersonB)
is also pointer to the instance. But @ObjectB
is the address of a pointer to the instance. That is there is an extra level of indirection.
FWIW both of these two options are meaningless and will not lead anywhere useful.
As for the different behaviour when you remove the virtual
directive, a virtual method is dispatched using the runtime type of the instance, a non-virtual method is dispatched using the compile time type of the instance variable. The fact that the code runs at all is due to the two unrelated classes having compatible VMTs. But that's just a chance of implementation detail.
Let's make a simple diagram (addresses are made up):
Address Value
+-----------------+
12345600 | Obj variable | 45680000
+-----------------+
|
v
+-----------------+
45680000 | instance |
| |
| |
+-----------------+
Obj
is a variable. It is an object reference, which means it is actually a pointer to the instance.
If you take @Obj
, you take the address of the variable. Its type is Pointer
.
So you get
@Obj = Pointer($12345600)
.
If you cast Obj
to Pointer
as Pointer(Obj)
, you get the address of the instance to which the variable points. Its type is Pointer
too.
So you get
Pointer(Obj) = Pointer($45680000)
.
You can even test this:
if @Obj = Obj then
Writeln('Same')
else
Writeln('Different');
if Pointer(Obj) = Obj then
Writeln('Same')
else
Writeln('Different');
and you should get:
Different
Same
I changed Object
to Obj
, because object
is a reserved word, in Delphi.
More info in my article on pointers: Addressing pointers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With