Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2012-12

Cast, inheritance, etc

5 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Marcos Douglas

Hi,

See the code below.
My question is: Why the cast worked, considering the c variable is a TAClass instance, not a TBClass.

-------------

program Project1;
{$mode objfpc}{$H+}
uses
  Classes, SysUtils;
type
  TAClass = class
  protected
    FInfoA: string;
  public
    constructor Create;
  end;
  TBClass = class(TAClass)
  protected
    FInfoB: string;
  public
    function GetInfoA: string;
    function GetInfoB: string;
  end;

{ TAClass }

constructor TAClass.Create;
begin
  FInfoA := 'TAClass info';
end;

{ TBClass }

function TBClass.GetInfoA: string;
begin
  Result := FInfoA;
end;
function TBClass.GetInfoB: string;
begin
  Result := FInfoB;
end;
var
  c: TAClass;
  s: string;
begin
  c := TAClass.Create;
  try
    // The c is a TAClass, not a TBClass instance!
    // but worked... WHY?!
    s := TBClass(c).GetInfoA;
    writeln(s);
    // Now I got an AV... ok for me.
    writeln(TBClass(c).GetInfoB)
  finally
    c.Free;
  end;
  writeln('Done');
end.

Marcos Douglas

Sven Barth
to Marcos Douglas
Marcos Douglas wrote 5 lines
Hi, See the code below. My question is: Why the cast worked, considering the c variable is a TAClass instance, not a TBClass.

As long as you don't access fields of TBClass inside of GetInfoA you won't run into problems, because FInfoA is accessed using an offset. If you want to catch such problems you should compile with "-CR". The output will then look like the following:

=== output begin ===

An unhandled exception occurred at $080482E8 :
EInvalidCast : Invalid type cast
$080482E8

=== output end ===

As a sidenote: as long as you don't access an object member at all you'd even be able to cast a primitive type like Integer to an object and call methods on it... (not that I'd suggest to do this... ;) )

Regards,
Sven

Marcos Douglas
to Sven Barth
Sven Barth wrote 28 lines
On 04.12.2012 20:03, Marcos Douglas wrote: > > Hi, > > See the code below. > My question is: Why the cast worked, considering the c variable is a > TAClass instance, not a TBClass. As long as you don't access fields of TBClass inside of GetInfoA you won't run into problems, because FInfoA is accessed using an offset. If you want to catch such problems you should compile with "-CR". The output will then look like the following: === output begin === An unhandled exception occurred at $080482E8 : EInvalidCast : Invalid type cast $080482E8 === output end === As a sidenote: as long as you don't access an object member at all you'd even be able to cast a primitive type like Integer to an object and call methods on it... (not that I'd suggest to do this... ;) ) Regards, Sven

Excellent explanation, thank you.

Marcos Douglas

silvioprog
to Marcos Douglas

2012/12/4 Marcos Douglas

Show quoted text 37 lines
On Tue, Dec 4, 2012 at 4:53 PM, Sven Barth wrote: > On 04.12.2012 20:03, Marcos Douglas wrote: >> >> Hi, >> >> See the code below. >> My question is: Why the cast worked, considering the c variable is a >> TAClass instance, not a TBClass. > > > As long as you don't access fields of TBClass inside of GetInfoA you won't > run into problems, because FInfoA is accessed using an offset. If you want > to catch such problems you should compile with "-CR". The output will then > look like the following: > > === output begin === > > An unhandled exception occurred at $080482E8 : > EInvalidCast : Invalid type cast > $080482E8 > > === output end === > > As a sidenote: as long as you don't access an object member at all you'd > even be able to cast a primitive type like Integer to an object and call > methods on it... (not that I'd suggest to do this... ;) ) > > Regards, > Sven Excellent explanation, thank you. Marcos Douglas

This is witchcraft! :)

Silvio Clécio My public projects - github.com/silvioprog
Chad Berchek
to Marcos Douglas: See the code below. My question is: Why the cast worked, considering the c variable is a…

This is an example of why it is usually wise to use the AS operator except in performance critical places. Using the parentheses notation is an unsafe cast; type checking does not occur at the time of the cast. This is especially dangerous because you run the risk of corrupting memory by writing to an address outside the block allocated for that object. Using the AS operator you will get an exception right away.

Instead of:

s := TBClass(c).GetInfoA;

you would write:

s := (c as TBClass).GetInfoA;

Personally I always use AS unless it is in a very performance critical area or I'm inside a block of code where I have already checked the type. For example people would usually not use the AS operator in an IF statement that checks the type:

if c is TBClass then
   s := TBClass(c).GetInfoA; // AS would be wasteful here
Marcos Douglas wrote 5 lines
Hi, See the code below. My question is: Why the cast worked, considering the c variable is a TAClass instance, not a TBClass.