Inheritance, VMT an so on...

Hi Xperts,

Although I'm into Delphi for a couple of years I'm still a little bit confused about the Delphi object stuff. As far as I know (and maybe I'm wrong) a constructor has to do the following:
- Allocate memory for the member variables
- Execute code of the "constructor procedure"
- Do some VMT stuff
And here my problem goes:
Assume you have a base type TBaseClass and a derived type TMyClass. This leads to something like

constructor TMyClass.Create;
begin
  inherited;
  SomeCode;
end;

destructor TMyClass.Destroy;
begin
  inherited;
end;

You have to get memory for the members of TBaseClass and the members of TMyClass. Dito for the VMT. What happens to the members and VMT of the base class if you do it like this

constructor TMyClass.Create;
begin
  // inherited;
  SomeCode;
end;

And what about that:

  myVar_1 := TMyClass.Create;
  myVar_2 := myVar_1.Create;

What if the constructors are virtual?

I hope you've understood what I'm asking for. If there is more than one good comments I will provide more points in an extra thread!

Thank you in advance!
LVL 2
j42Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

soapsiamCommented:
VMT is specific for each class not object (instance of the class). So there is only one VMT per class and there is a pointer to base class's VMT (in case of root class like TObject pointer to Ancestor VMT is NIL). Thus there is notthing with VMT. If you call
  obj := TMyClass.Create;

  First the constructor of TMyClass was called, then in the constor of TMyClass, Ancestor's constructor was called (denoted by inherited reserved word).

myVar_1 := TMyClass.Create; // Delphi way of constructor called to create instance of that class
 myVar_2 := myVar_1.Create;

Virtual means you can override it in descendant class.

Hope you understand me...
0
j42Author Commented:
Hi soapsiam,

thanks for answering! I guess you want to provide some basic information about OOP but the information I need is "a little bit more technical" (sorry, it is so hard for me to explain that in english sometimes):

> Thus there is notthing with VMT. If you call
>   obj := TMyClass.Create
If you do not call TMyClass.Create at any time in your application there is no need for the linker to create a VMT for TMyClass

> First the constructor of TMyClass was called, then in
> the constor of TMyClass, Ancestor's constructor was
> called (denoted by inherited reserved word).
But what if I omit the inherited key word? What exactly will be omitted? The code you can see between "begin" and "end;" of TBaseClass.Create? What else?

> myVar_1 := TMyClass.Create; // Delphi way of constructor called to create instance of that class
> myVar_2 := myVar_1.Create;
The second line is valid (even though not very usual). myVar_2 will contain adress of myVar_1. But what else happens, any memory allocation???



Regards
(I hope this is not to confusing)
0
andrewjbCommented:

When you call a constructor using a class reference
e.g. TMyClass.Create
the compiler provides you with the appropriate amount of memory for the object, and then calls the constructor.

You can actually call the constructor using an object reference. In this case, the compiler does NOT allocate the memory - you're really just calling a method of the object

so var X : tMyClass;

X = .... something

X.Create - is valid, but just calles the 'Create' method in teh same was as any other procedure or function.


When you use inherited within a constructor, it means 'call the parent's method with the same name as this one'

So it's like calling Self.Create, but making sure you get the parent class's constructor and not yours.

So, if you don't call 'inherited', you still get all the appropriate memory allocated for the class itself, but won't get any parent constructors called.

This matters if the parent constructor creates some objects iteself, or initialises variables

e.g.

tBaseClass
  private:
    fList : tStringList;

constructor tBaseClass.Create()
begin
  fList := tStringList.Create;
end;


tSubClass = class(tBaseClass)
...

constructor tSubClass.Create()
begin
 ... do stuff
  inherited; //// ****
end;

if i don't use 'inherited', then the fList won't get created and that's going to mess things up later...
0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

j42Author Commented:
Hi andrewjb,

thank you, the penny has dropped! As I understood, you can omit the inherited in the constructor as long as you know what you do. But what about the destructor? I do not think it possible to call it like a 'normal' procedure/function?
Can you tell me any reason why the destructor is virtual if you use the code insight feature.
Can you tell me why the 'inherited' never is omitted if you use class completion (Ctrl-Shift-C)?
Thanks so far (you really helped me a lot)!



Regards
J
0
andrewjbCommented:
The destructor is similar, in a way. Calling a destructor runs the code you've written, then 'magically' frees off the memory allocated for the object. You don't have to call the inherited destructor. If you don't, the memory is still freed off properly, but code written in inherited destructors are not.
This is a problem if one of those inherited destructors is freeing off something extra itself. Back to the example of a tStringList. If the base class constructor is doing
fList = tStringList.Create;
then this is creating a new object. You therefore need to free this off in the destructor
destructor tBaseClass.Destroy;
begin
  fList.Free;
end;

The 'magic' memory freeing applies to the fList POINTER, but not the object that you've created.

So, in general, you don't actually have to call the inherited destructor, but I can't really think of a reason why you might want to avoid it.


Why is it virtual? Because most of the time you destroy objects using 'Free' instead of 'Destroy', and quite often you destroy objects using a variable which is pointing to a base class. Both of these need a virtual mnethod.

The definition of 'Free' is roughly

procedure tObject.Free;
begin
  if ( Self <> nil )
    Self.Destroy;
end;

If the destructor wasn't virtual, that would only call tObject.Destroy whereas you want it to call txxx.Destroy - the bottom of the class structure.

Consider too:

tBaseClass = class(tObject)
...
destructor Destroy; override;

tClassOne = class( tBaseClass )
....
destructor Destroy; override;

tClassTwo = class( tBaseClass )
...
destructor Destroy; override;


then

var x,y : tBaseClass

x := tClassOne.Create;
y := tClassTwo.Create;

x.Destroy; (or x.Free;)
y.Destroy;

when you call x.Destroy you've a variable pointing to tBaseClass, but you want to have tClassTwo.Destroy called. Similar for y. Hence you want virtual destructors.


Hope that makes enough sense to be useful!

Andrew.
0
j42Author Commented:
Hi Andrew,

Since you did a lot of writing I have increased points :-)

> Why is it virtual? Because most of the time you destroy
> objects using 'Free' instead of 'Destroy', and quite
> often you destroy objects using a variable which is
> pointing to a base class. Both of these need a virtual
> mnethod.
Hooray, I've got it!

> The destructor is similar, in a way. Calling a
> destructor runs the code you've written,
> then 'magically' frees off the memory allocated for the
> object.
Sorry, I did not provide you with enough information. What I intended to ask was if it is possible to call the destructor without actually destroying the object - like calling the constructor without creating an object.



Thanks so far
J
0
andrewjbCommented:
Call the destructor without destroying the object?

No - you can't. The 'magic' memory freeing always happens.


Another thing:

If a constructor fails for some reason (e.g. not enough memory, or an exception is raised) then the destructor gets called. However, note that not everything might have been created yet.

e.g.

if your constructor contains

fList1 := tStringList.Create;
{Do something else}
fList2 := tStringList.Create;

and the {something else} causes an exception, then the destructor gets called, but only fList1 has been created

So, that's why you typically use 'Free' instead of destroy, in destructors...

destructor tMyClass.Destrot;
begin
  fList1.Free;
  fList2.Free;
  inherited;
end;

and you don't need to worry whether fList1 and 2 have actually been created yet. (All variables are initialised to 'nil', so you can be sure that fList2 is still 'nil'.

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
j42Author Commented:
Thanks a million!

> So, in general, you don't actually have to call the
> inherited destructor, but I can't really think of a reason
> why you might want to avoid it.
Just to satisfy curiosity...

0
andrewjbCommented:
No probs!

Hey - and constructors need to be virtual if you're building components to go on a form (anything derived from TComponent or a child) because of the way Delphi creates them...

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.