Delphi classes_(re)

Tue, 8 Jul 1997 01:18:41 +0200 (MET DST)


According to The African Chief:
> 
> If you don't use "override", then it creates a new method by the 
> same name, which replaces the original one. The full implications
> of this are not clear to me yet, but they have caused me some
> grief a few times (things not behaving as you expect).

But I think that I have understood it now.  :-)

While `override' overrides a virtual method, i.e. replaces its entry
in the VMT, `virtual' would create a new entry in the VMT, thus making
the old method inaccessible - except when using this class through a
pointer to a parent class.

One example (I hope there aren't too many typos):


    Type

    FooClass = class
      Constructor Create;
      Procedure Bar; virtual;
    end (* FooClass *);

    Foo2Class = class ( FooClass )
      Procedure Bar; override;
    end (* Foo2Class *);

    Foo3Class = class ( Foo2Class )
      Procedure Bar; virtual;
    end (* Foo3Class *);

    Foo4Class = class ( Foo3Class )
      Procedure Bar; override;
    end (* Foo4Class *);

    Var
      Foo: FooClass;
      Foo2: Foo2Class;
      Foo3: Foo3Class;
      Foo4: Foo4Class;

    ...

    Foo4:= Foo4Class.Create;
    Foo3:= Foo4;
    Foo2:= Foo4;
    Foo:= Foo4;

    Foo4.Bar;  (* calls Foo4Class.Bar                               *)
    Foo3.Bar;  (* calls Foo4Class.Bar which overrides Foo3Class.Bar *)
    Foo2.Bar;  (* calls Foo2Class.Bar                               *)
    Foo1.Bar;  (* calls Foo2Class.Bar which overrides FooClass.Bar  *)


That's my guess.  If somebody really *knows* it, please tell me!

> given this; "Procedure Foo (Bar:TObject)"
> 
> In Delphi, Bar is a  pointer (even though it is automatically deferenced). 
> In BP it is not a pointer (you would have to do "Bar: pObject", and then
> do some magic to dereference it automatically, to achieve the same
> thing).

In BP (and GPC), you have to write "Procedure Foo ( Var Bar: tObject )"
to get (almost) exactly the same:  `Var' parameters are internally passed
through a pointer which is implicitly dereferenced whenever you access
the parameter.

> >Sounds logical. But when does Delphi create objects by itself?
> 
> When it manages the project, e,g., when you drag objects from
> the repository onto your form - which is how most Delphi programs
> are meant to be written.

Then it's not Delphi (the compiler) who creates and destroys the objects
but VCL which has this close interaction with the development tools.

> Well, "Free" is written in ASM. So, maybe therein lies the magic.
> I don't understand the ASM code, so I can't really comment on this.

Well, as long as you know *exactly* what the compiler does, you can write
and use `Free' - perhaps even as a virtual method.  But I remember well
a rather strange bug in one of my programs when I upgraded from TP6 to BP7
without replacing my heavily-modified Turbo Vision with the new version,
and I finally discovered the following in `menus.pas' from Turbo Vision:


    function NewItem(Name, Param: TMenuStr; KeyCode: Word; Command: Word;
      AHelpCtx: Word; Next: PMenuItem): PMenuItem;
    var
      T: PView;
      P: PMenuItem;
    begin
      if (Name <> '') and (Command <> 0) then
      begin
        New(P);
        P^.Next := Next;
        P^.Name := NewStr(Name);
        P^.Command := Command;
        P^.Disabled := not T^.CommandEnabled(Command);
        P^.KeyCode := KeyCode;
        P^.HelpCtx := AHelpCtx;
        P^.Param := NewStr(Param);
        NewItem := P;
      end else
      NewItem := Next;
    end;


Note that "T^.CommandEnabled(Command)" is called with `T' being an
*uninitialized* local variable!  So it was quite natural that my
BP7-compiled protected-mode program barfed.  But why did the same
program accept it when compiled with TP6?  Well, `CommandEnabled' is
not a virtual method, and the body of `CommandEnabled' does never refer
to any data fields of the object.  (So why it's a method then???)
Everything which happens with the implicit `Self' parameter then is that
it is loaded into the `es:di' register pair ... which doesn't matter in
real mode but causes a protection fault in protected mode if the `es'
register does not get a valid selector.  That's why the BP7 program
crashed.

So, everybody, don't use uninitialized local variables, especially not
pointers!  Yes, Borland, you too!

How did Borland solve this problem in BP7?


    const
      T: PView = nil;

    ...
        P^.Disabled := not T^.CommandEnabled(Command);
    ...


It's great, isn't it?  Now you see why I do not want to trust Delphi and
VCL and prefer to hack GPC instead ...

Greetings,

    Peter

 Dipl.-Phys. Peter Gerwinski, Essen, Germany, free physicist and programmer
peter.gerwinski@uni-essen.de - http://home.pages.de/~peter.gerwinski/ [970201]
 maintainer GNU Pascal [970624] - http://home.pages.de/~gnu-pascal/ [970125]


Peter Gerwinski (peter@agnes.dida.physik.uni-essen.de)

HTML conversion by Lluís de Yzaguirre i Maura
Institut de Lingüística Aplicada - Universitat "Pompeu Fabra"
e-mail: de_yza@upf.es