Solved

Is it ok to have a class with no parent?

Posted on 1998-09-16
14
250 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 3
  • 3
  • +2
14 Comments
 

Expert Comment

by:mazzini
ID: 1339945
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
ID: 1339946
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
ID: 1339947
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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 5

Expert Comment

by:JimBob091197
ID: 1339948
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
ID: 1339949
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
ID: 1339950
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
ID: 1339951
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
 
LVL 2

Expert Comment

by:gysbert1
ID: 1339952
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
ID: 1339953
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
ID: 1339954
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
ID: 1339955
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
ID: 1339956
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
ID: 1339957
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
ID: 1339958
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

Enroll in June's Course of the Month

June’s Course of the Month is now available! Experts Exchange’s Premium Members, Team Accounts, and Qualified Experts have access to a complimentary course each month as part of their membership—an extra way to sharpen your skills and increase training.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…

719 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