Reputation: 3238
I have an app which uses class, a base class called TBaseDB, and there will be many descendants of TBaseDB, all DIRECT siblings, only one of which has been started now, TOraDB, but will later add TSQLDB and others.
My app uses one instance of the class, and it is a global instance, i.e. a global variable called PROJ. There is an issue in my understanding of constructors, destructors, and global variables which is causing EStackOverflow somewhere else in the app. If I comment out my PROJ.CREATE, the EStackOverflow goes away.
My constructors ONLY set variables, they do not dynamically create linked lists, arays, or other memory intensive objects.
Here are some code snippets.
// global var definition
// Specifically of BASE class, so I can call any child class without knowing which child class it is...
PROJ : TBaseDB;
My routine which causes my error...
procedure TForm1.ShowBug;
begin
// We have clicked on 'Create New Oracle Project
// Now create our PROJ object.
// This is defined globally, but MAY have been used before
// so 'zero' it out
FreeAndNil(PROJ);
// Note that the create is on the CHILD class, not the BASE class
// If I DON'T create the class, no error....
PROJ := TOraDB.Create;
// ... other code
end;
Here are my class definitions.
Type
TBaseDB = class
published
public
DAC_ID: Integer;
DAC_ShortName : String;
DAC_LongName: String;
Constructor Create;
Destructor Destroy; override;
... other stuff
end;
implementation
// TBaseDB /////////////////////////////////////////////////////////////////
constructor TBaseDB.Create;
begin
inherited;
end;
destructor TBaseDB.Destroy;
begin
// If I comment out next two lines, my issue goes away
// but shouldn't I have them....?
Self.Free;
Self := nil;
// Always call the parent destructor after running your own code
inherited;
end;
Here is my definition for the TOraDB class
Type
TOraDB = Class(TBaseDB)
public
Constructor Create;
Destructor Destroy; override;
... other stuff
End;
implementation
// ------------------------------------------------------------------------------
constructor TOraDB.Create;
begin
inherited;
// Now set up the information about the source database. We know it is Oracle
// even though we DONT know if it is connected
DAC_ID := 4;
DAC_ShortName := 'Ora';
DAC_LongName := 'Oracle';
end;
// -----------------------------------------------------------------------------
destructor TOraDB.Destroy;
begin
// Always call the parent destructor after running your own code
inherited;
end;
I am not understanding something about 'resetting' a global class variable. Where should I be 'resetting it, so I can still use the GLOBAL variable PROJ?
Thanks,
GS
Upvotes: 2
Views: 249
Reputation: 19106
You must not call Self.Free
in the destructor of a class.
Free calls Destroy and Destroy calls Free ...... until Stack Overflow
destructor TBaseDB.Destroy;
begin
// Don't do that at all in a destructor
// Self.Free;
// Self := nil;
// Always call the parent destructor after running your own code
inherited;
end;
TObject.Free
is just a safe call of the destructor, because it will test if the Instance is not nil.
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
EDIT
Regarding the global variable PROJ
there is a simple (but not very wise) solution
destructor TBaseDB.Destroy;
begin
if Self = PROJ then
PROJ := nil;
// Always call the parent destructor after running your own code
inherited;
end;
You should have a look at Singleton implementation instead.
Upvotes: 12