Link to home
Start Free TrialLog in
Avatar of j42
j42

asked on

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!
Avatar of soapsiam
soapsiam

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...
Avatar of j42

ASKER

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)

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...
Avatar of j42

ASKER

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
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.
Avatar of j42

ASKER

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
ASKER CERTIFIED SOLUTION
Avatar of andrewjb
andrewjb
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of j42

ASKER

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...

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...