Solved

Is it ok to have a class with no parent?

Posted on 1998-09-16
14
243 Views
Last Modified: 2010-04-06
I am tryingh to track down some memory leaks in my project using Memory Sleuth. I am wondering if there is a problem declaring (formless) classes having no parent, thereby not calling an inherited Create and Destroy in the constructor and destructor respectively?
0
Comment
Question by:tomcorcoran
  • 5
  • 3
  • 3
  • +2
14 Comments
 

Expert Comment

by:mazzini
Comment Utility
Even if you don't declare the parent class explicitly, Delphi will do it for you, assigning your new class as a child of TObject. So, every class yout declare in your programa will allways be an indirect or direct descendent of TObject, wich Create and Destroy methods will be used, if you don't declare your own ones.
BUT, the Create method of TObject takes care only of basic memory assigned to the class. This means that if you don't initialize and dispose declared memory areas by means of a new Create and/or Destroy method, you may have problems (a full hand of them ...)

mazzini
0
 

Author Comment

by:tomcorcoran
Comment Utility
Mazzini, interesting, though I still know if what I have done is okay. Here's an example:

  TMacLog = class
  private
    { Private declarations }
    unIni: TMainIni;
    purgeDays: integer;
    bkmLog: TBookMarkStr;
    procedure Purge(beforeDate: TDateTime);
  public
    tblMacLog: TTable;

    constructor Create;
    destructor Destroy;

    procedure UserPurge;
    procedure LogStart(code: string; runType: integer);
    procedure LogFinish(userCancel: boolean);
  end;

implementation

uses DataDict, Utils, MacPurge, Frame;

constructor TMacLog.Create;
begin
   tblMacLog := TTable.Create(Application);
   unIni := frmFrame.ReturnIni;
end;

destructor TMacLog.Destroy;
begin
   tblMacLog.Free;
end;
..

Do I need to include the inherited Creates and Destroys?

Thanks, Tom.

0
 
LVL 5

Expert Comment

by:JimBob091197
Comment Utility
Hi Tom

> Do I need to include the inherited Creates and Destroys?

In your particular example, TMacLog inherits directly from TObject ("TMacLog = class" is the same as "TMacLog = class(TObject)".)

So in your particular case, you don't need to use "inherited" in Create & Destroy.  If you look in System.pas (Delphi source) you will see that TObject's Create & Destroy do nothing.

BUT you should get into the habit of always calling inherited in constructors (Create) & destructors (Destroy).  It won't do any harm for TMacLog, but (for example) if you inherit from TComponent you MUST call inherited in Destroy else memory won't be freed.

Cheers,
JB
0
 
LVL 5

Expert Comment

by:JimBob091197
Comment Utility
Also, in your class definition you should override the destructor (and the constructor if it is virtual, which it isn't for TObject).

E.g.
TMacLog = class
..
  destructor Destroy; override;
..
end;

This ensures that the correct destructor gets called for the object, especially if you are type-casting objects in your code.

JB
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
Please see my response to the other question ...

"         tblMacLog := TTable.Create(Application);   "

this bothers me. If tblMacLog is not a pointer it should not be necesarry to call Create.
It WILL be called automatically by Delphi. This is caused by marking the method as being the constructor. Calling create yourself if Delphi already did it automatically is one of the most common sources of leaks ! I have not used TTable so correct me if I am wrong.

As stated in my other reply it is not a good idea to create objects in constructors. If the create fails the the table object will be corrupt and your code will crash. Use an init function and a destroy function to do everything that can possibly fail ...


0
 
LVL 5

Expert Comment

by:JimBob091197
Comment Utility
gysbert1: I'm not sure I understand you.  Are you saying that tblMacLog will be automatically created in TMacLog's constructor?
0
 

Author Comment

by:tomcorcoran
Comment Utility
Gysbert1, tblMacLog will not be created automatically, I create it at run-time, it's not dropped on the form )otherwise it would be in the protected section). You mention another question/reply but I have no idea where you are talking about.

JimBob, thanks, good information, so you are saying if I declare a destructor I should always oevrride it? I Don't understand the virtual contructor concept you mention - I use constructors  (and destructors) all the time in my forms but never override them. Thanks, Tom.
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
If tblMacLog is not a pointer to a class but an instance of the class itself its construcor and destructor will be called automatically, just like you do not have to say purgeDays = TIteger.Create() (I know an integer is not a class but i guess it will give you the Idea)

Just like global and local variables differ, the memory local ones occupy gets freed as they go out of scope, instances of classes differ as well. If you declare:

type
 p_tblMacLog  = ^TTable;

var
 tblMaclog  : p_tblMacLog;
   .
   .
   .

then you will have to say

 tblMacLog = TTable.Create();

As I said I am not sure if TTable is a pointer type or a class type, but generally built in pointer types has a P hidden somewhere in the name (eg. PTable). I do not have Delphi on my machine right now so it is a bit difficult to check for you ...

If I have e.g.

 TClass1 = class
        private
          { Private declarations }
          int1: integer;
          int2: integer;
 end;

 TClass2 = class
        private
          { Private declarations }
        Count : integer;
        Instance1 : TClass1;
 end;

function MyFunction();
var
  MyInstance  : TClass2;
begin
    // Here I can use MyInstance
    //  I do not have to call MyInstance.Create(); Delphi calls it for me
   MyInstance.Instance1.int1 = 123;

   // I do not have to call MyInstance.Free() or MyInstance.Destroy() to free the memory
  //  It is done by Delphi for me.
end;

Note that I did not implement a constructor and destructor so only Create() and Destroy() of TObject (the base class) will be called by Delphi. It would not make ANY difference however if I had done so as you have above.

Try popping a modal messagebox up in every constructor and destructor and then you will know exactly when it is called ! Count the number of "Constructed !" messages and the number of "Destructed" messages and you will immediately know if that is your problem !

Good Luck ...
0
 

Author Comment

by:tomcorcoran
Comment Utility
Gysbert1,

You have it completely wrong I am afraid. The VCL is such that components references are pointers and do not need to be dereferenced. I appreciate the comments but you should probably avoid commenting on Delphi if you don't use it? TTable needs to be created in this instance, of that there is no doubt, there is nothing to check for me, let's get back to the original question, thanks, Tom.
0
 

Author Comment

by:tomcorcoran
Comment Utility
Gysbert1,

You have it completely wrong I am afraid. The VCL is such that components references are pointers and do not need to be dereferenced. I appreciate the comments but you should probably avoid commenting on Delphi if you don't use it? TTable needs to be created in this instance, of that there is no doubt, there is nothing to check for me, let's get back to the original question, thanks, Tom.
0
 

Accepted Solution

by:
dksqrt earned 50 total points
Comment Utility
1. "TMacLog = class" in Delphi is equivalent to TMacLog = class(TObject). BTW this was not the case in Pascal.

2. You should *always* use "destructor Destroy; override;",
otherwise (even if you have no descendants from your class and always use direct declarations of TMacLog instances) your desctructor will not be called during any generic objects operations, for example if you free all instances of TList participants, etc. You just must... The Destroy is a kind of system thing... See 4.

3. JimBob's comment about inherited constructors and destructors calls is good. You don't have to call them in your case, but this would be the need for all descendants and you can even include such calls in keyboard templates ;-)
Constructor TSome.Create;
begin
  inherited Create;
  ...
end;

Donstructor TSome.Destroy;
begin
  ...
  inherited Destroy;
end;

3a. You will need to use virtual constructors only in some very special cases. TObject has normal Create. AFAIR TPermanent has virtual Create. One example if you use metaclasses (class references) to create instances like that
MetaClass : TClass;
.
Var := MetaClass.Create;
- it's really better to use virtual constructors

3b. If you inherit from a class with virtual constructor and accidentaly write
.
  public
    ...
    constructor Create;
Delphi will issue warning - you are about to create the *different* procedure with the same name!  It's possible - you can ignore warning but it's better to write
   constructor Create; override;
in that case

4. I am sorry but gysbert1's comment is *totally* misleading:
   - no constructors are called automatically. Never! It's not C++! All internal pointer fields in class like "tblMacLog: TTable;" will be nil. (all class fields are filled by zeroes in "system" part of constructor, i.e. before constructor's begin)
   - you can have a dozen of constructors and call them anyway you like it
   - if Create fails your destructor (virtual *Destroy*!) will be called on a partially constructed object. This is a quite normal situation. That's why you need to call Free procedure in a destructor. Free is a very special *class procedure* - it could work on nil-s. It's inline assembler - look in SYSTEM.PAS...
0
 
LVL 2

Expert Comment

by:gysbert1
Comment Utility
I sincerely apologize !!

I have been using C++ Builder for the last 6 Months in stead of Delphi for various reasons.

Although C++ Builder also uses the (Delphi, pascal coded) VCL it is obviously implemented differently. Constructors is C+= Builder (as in C++) are called automatically and that lead me to my answer (due to an incorrect assumption that it is the same in Delphi ...)

I would have to brush up a bit before making a fool of myself again ! ...

My other comment is valid though. If a single call inside the constructor fails eg. a GetMem, the constructor will succeed. This is of course not such a big issue if you call the constructor yourself as in Delphi, but since the constructor cannot return a result (such as -1 on errors) you have no way to know if the GetMem failed or not, but that is something completely off this subject so we will leave it there.

Sorry once again if I was misleading ...

0
 

Author Comment

by:tomcorcoran
Comment Utility
Gysbert1, I did find your other comment interesting, I was not aware that it is not a good idea to create objects in constructors and would like to read up on it.

Dksqrt, thanks, what do you mean by " and always use direct declarations of TMacLog  instances". Interesting information - where can I learn more about metaclasses? I will make sure all my destructors have override. Tom
0
 

Expert Comment

by:dksqrt
Comment Utility
1. By "and always use direct declarations of TMacLog instances" I meant if you do not have any descendants AND do not use metaclasses i.e. if you have only static-linked TMacLog instances. When your Obj.Destroy will be static-linked and work (though I think that Obj.Free will not work properly in that case). Anyway do not create problems yourself - "Destroy; override;" is a must!

2. Gysbert1,- if constructor fails (i.e. raises unhandled exception) in Delphi a lot of things happen AFAIR:
a. Destructor Detroy is called on a partially constructed object.
b. Exception propagates.
c. AFAIR the result is nil.
Therefore you can determine this situation with ease by catching an exception and by comparing result (Obj := TSomeClass.Create; ... if Obj<>nil ...). Usually exception catching is used...

3. Metaclasses (class references) - see Delphi help on "TClass", "class of", "class procedure", "class function" or Delphi documentation if available. Browsing SYSTEM.PAS and other VCL sources could also help a lot.
I want to add one comment: usually virtual constructors are used in the same situation as virtual procedures - if you do not know the real class when you call them. It's just a rare situation if you don't know the class at the moment when you create an instance. But sometimes it happens...
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Suggested Solutions

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now