Delphi classes (Was: bugs, incompatibity, function attributes)_(re)

Thu, 3 Jul 1997 11:13:17 +0100 (BST)


On Thu, 3 Jul 1997 01:13:18 +0200 Frank Heckenbach  wrote:
>
>The African Chief wrote:
[...]

>> 2. [a] a Class instance becomes a pointer to the class;
>>     (or is it that the Class itself is a pointer to an object/class?);
>
>I think the latter would be a more consistent interpretation (at least for
>us, even if Delphi does it otherwise). So "class" = "pointer to object",
>approximately...

I agree. I think this is how Delphi does it too. 
"Procedure Foo (Bar: TObject)" in Delphi is
the same as "Procedure Foo (Bar: pObject)" in BP.
However, Delphi automatically deferences "Bar"
every time it is referred to.

>> 	e.g.,  "Var Foo : MyFoo"
>> 	=       "Var Foo : ^MyFoo" in the current OOP model
>
>Saves declaring those "PFoo" types, OTOH makes it impossible to declare
>global or local objects variables (without the overhead of allocating
>memory). If we have objects and classes, the programmer can choose what
>(s)he prefers.

Delphi also supports both Objects and Classes - so it would be 
nice if we could as well.

>>    [b] the constructor does the heap allocation stuff,
>> 	e.g., "Foo := MyFoo.Create"
>> 		*allocates memory for Foo "(New, Foo)"
>> 		*calls Foo^.Create (constructor)
>
>So it's the same as "Foo := New(MyFoo, Create)" in BP? 

More or less.

>What about destructors? Does Delphi still use "Dispose", or has it another
>syntax too, or does it dispose of objects automatically. 

According to the docs, the destructor destroys the object
and releases the memory allocated to it. You are advised
that you seldom need to call the destructor directly, and 
that you should call the "Free" procedure instead, to release 
memory and dispose of the object. It says that "Free" checks 
to see if the pointer is nil before calling Destroy and is also
more efficient in code size.

However, when you look at the code for TObject.Destroy,
it actually does nothing. Same thing with the constructor
(TObject.Create). Thus all the memory allocation and 
deallocation stuff  seems to be done with compiler magic. 
TObject.Free contains ASM code which tests what is in 
EAX (I am talking about 32-bit Delphi here), and does some 
other stuff with EAX and ECX and then does something with
the VMT which I assume is a call to the ECX. "Destroy"
destructor. Those of you who know Intel asm, will probably
understand all this.

>(C++, BTW, does the latter. It destroys e.g. local objects at the end of the function
>automatically. 

Delphi destroys all objects that it creates itself. However,
if you manually create an object by calling its constructor,
you have to destroy it yourself with a call to "Free".

>This is possible because in C++ every class has exactly one
>destructor. I guess they set the pointer to nil automatically at the
>beginning (and set it back to nil if the destructor is called manually),
>so they can easily find all local objects that "exist" and destroy them.)

I suspect that Delphi sets them to Nil as well, both before
construction and after destruction - otherwise the checks for
"Nil" made by TObject.Free would not make any sense, since
there is nowhere in the source code where these things are
manually set to Nil.

>> 		*Foo now points to whatever
>>                        "Foo.Create" - should generate an exception
>>                        because Foo needs to be assigned to the
>> 	       constructor
>
>AFAICS, calling a second constructor does not even have to be forbidden --
>at least in gpc. Constructors can be called like normal methods from an
>initialized object, though this is certainly uncommon practice.

Probably so. However, Classes are different from "mere" objects,
and I suspect that with Delphi, (assuming that Foo is an instance 
of a Class, and not of a mere object) if you do;

   New(Foo);
   Foo.Create;

What "Foo" is pointing to may actually be undefined - and you will 
certainly get an exception when you try to do anything with "Foo".

That may be why you need to assign "Foo" to its constructor - viz;
   Foo := TMyFoo.Create;

>> 4.  Virtual methods to be overridden must be overriden
>> with the "override" directive; the "virtual" directive merely
>> creates a new method (or at least, don't reject the
>> "override" directive in a "Class" definition)
>
>Does this mean you have to use "virtual" for *every* method in Delphi, or
>what is meant by "create a new method"?

Somebody else (Marius Gedminas) has responded to this.

>> 5. Allow "Published" methods (or at least, don't reject the
>> "published" directive in a "Class" definition).
>
>Is this the same as "public" in C++ (= visible to everyone)? I, personally,
>would prefer "public", "protected" (=visible only to descendants) and
>"private" (=visible only to the type/class itself -- BTW, BP does it a bit
>differently, and extend the visibility of private methods to the whole unit
>it's declared in. I don't see any reason or advantage of this). So, perhaps
>we could allow all those keywords, including "published", in any order, also
>possibly repeated (unlike BP).

And this too.

>> 6. All classes to have the same ultimate ancestor object.
>
>The same discussion as with objects recently... ;-)

Yes ;)

>Is this really necessary (if so, for what?), or is it sufficient if all
>classes in a given library use the same ancestor by convention?

In this case, it *is* necessary for Delphi compatibility. In Delphi
all classes are descendants of TObject. Doing this in Delphi;

Type MyFoo = Class
end;

is exactly the same as doing this;

Type MyFoo = Class(TObject)
end;

The VCL depends on this being so, and we should also
support it (not least because Delphi programmers expect
this to be so). This means of course that TObject then has
to become part of the GPC RTS. For present purposes, it
only needs to be something like this;

Type TObject = Class
   Constructor Create; virtual; 
   Destructor  Destroy; virtual;  
   Procedure Free; virtual;
end; {I am not sure about the "virtual" bit}

{and any other (private) stuff needed for any compiler
magic that we need to do; Delphi contains some more
stuff in TObject - most of them are all ASM, and I don't
have a clue as to what they do }.

Like I said, "Create" and "Destroy" are empty in TObject
(i.e., they contain just an empty "begin end;" block).
"Free" checks whether the pointer is Nil, and if it is not, 
it calls Destroy. So, TObject itself is easy to implement - 
but I don't know what compiler magic will need to be done
to make sure that everything else works as expected.

Best regards, The Chief 
Dr Abimbola A. Olowofoyeku (The African Chief, and the Great Elephant)
Author of:  Chief's Installer Pro v3.60 for Win16 and Win32.
Homepage:  http://ourworld.compuserve.com/homepages/African_Chief/
E-mail: laa12@cc.keele.ac.uk



The African Chief (laa12@cc.keele.ac.uk)

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