user1009073
user1009073

Reputation: 3238

Delphi Class def causing EStackOverflow

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

Answers (2)

Keith Miller
Keith Miller

Reputation: 1768

Don't use:

  Self.Free;
  Self := nil;

in your destructor.

Upvotes: 2

Sir Rufo
Sir Rufo

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

Related Questions