andrewjackson
asked on
How to prevent Constructor from instantiating an Object
I've got a class (TMyObject) that inherits from TObject.
I want the TMyObject.Create method to be intelligent enough to not instantiate an object if certain conditions apply. I've tried raising exceptions and calling 'abort' from within the constructor which just seems to force the destructor method to be called followed by spurious AV's
elsewhere in my code...
constructor TMyObject.Create;
begin
if <some condition> then
raise Exception()
{ or call 'Abort' }
else
inherited Create;
end;
Does anyone know of a clean way to cleanly quit a constructor method and return 'nil' rather than an instance of TMyObject?
Thanks
I want the TMyObject.Create method to be intelligent enough to not instantiate an object if certain conditions apply. I've tried raising exceptions and calling 'abort' from within the constructor which just seems to force the destructor method to be called followed by spurious AV's
elsewhere in my code...
constructor TMyObject.Create;
begin
if <some condition> then
raise Exception()
{ or call 'Abort' }
else
inherited Create;
end;
Does anyone know of a clean way to cleanly quit a constructor method and return 'nil' rather than an instance of TMyObject?
Thanks
ASKER
I've not tried but surely its not correct to 'override' a constructor. A constructor must be a 'class method' rather than an 'object method' since its called before the object is instantiated. So there can't be a reference to that method in the virtual method table for that object at the point when its called.
Hi,
I haven't tried this very thoroughly but it seems to work, however this is probably not the recommended way to do it:
constructor TMyObject.Create;
begin
if Condition then
begin
Self := nil; //<<<
Abort;
end else inherited Create;
end;
Call it like this:
try
MyObject := TMyObject.Create;
except // MyObject = nil
end;
I tried to instantiate 10000 objects and it didn't consume any stray memory at all.
/// John
I haven't tried this very thoroughly but it seems to work, however this is probably not the recommended way to do it:
constructor TMyObject.Create;
begin
if Condition then
begin
Self := nil; //<<<
Abort;
end else inherited Create;
end;
Call it like this:
try
MyObject := TMyObject.Create;
except // MyObject = nil
end;
I tried to instantiate 10000 objects and it didn't consume any stray memory at all.
/// John
Have you tried using a Class Method other than a constructor??
class function TSpecial.CreateSpecial: TSpecial;
begin
Result := nil;
if <MyCondition> then
raise ECreateSpecial //or you could just return nil
else
Result := TSpecial.Create;
end;
Rick
P.S. You can override a constructor...how do you think they created...Create(Owner: TComponent);
From the Delphi help
public
constructor Create(Owner: TComponent); override;
Like other methods, constructors can be virtual. When invoked through a class-type identifier, as is usually the case, a virtual constructor is equivalent to a static constructor. When combined with class-reference types, however, virtual constructors allow polymorphic construction of objects--that is, construction of objects whose types aren't known at compile time, as described in ...
class function TSpecial.CreateSpecial: TSpecial;
begin
Result := nil;
if <MyCondition> then
raise ECreateSpecial //or you could just return nil
else
Result := TSpecial.Create;
end;
Rick
P.S. You can override a constructor...how do you think they created...Create(Owner: TComponent);
From the Delphi help
public
constructor Create(Owner: TComponent); override;
Like other methods, constructors can be virtual. When invoked through a class-type identifier, as is usually the case, a virtual constructor is equivalent to a static constructor. When combined with class-reference types, however, virtual constructors allow polymorphic construction of objects--that is, construction of objects whose types aren't known at compile time, as described in ...
ASKER
I do actually use a Class Method (or something like it) but that still relies on other developers remebering to only instaniate TSpecial using that method rather than the constructor. There is no way (that I know of) of hiding / making private the constructor and thereby preventing it being called so that's why I'm trying to catch the error in the constructor.
FYI
With this code I'm actually trying to implement the 'singleton' design pattern in Delphi. This is a pattern that permits only one instance of an object that is globally accessible to all other objects in the system. So the object gets instantated in the 'initialization' section of the unit that it is contained within and destroyed in the 'finalization' section of that unit. The one instance gets assigned to a global variable.
The 'condition' in my constructor just checks if that global variable equals nil or not (i.e. whether the only allowable instance has been created or not). My implementation is based on a couple of examples that I've seen on the web but which both fail because of this constructor problem.
erajoj
I tried your solution but still got AVs. I wonder is it because I raise an exception / abort during the initialization part of my code. I presume your tested involved creating 1000 objects AFTER the program had loaded.
FYI
With this code I'm actually trying to implement the 'singleton' design pattern in Delphi. This is a pattern that permits only one instance of an object that is globally accessible to all other objects in the system. So the object gets instantated in the 'initialization' section of the unit that it is contained within and destroyed in the 'finalization' section of that unit. The one instance gets assigned to a global variable.
The 'condition' in my constructor just checks if that global variable equals nil or not (i.e. whether the only allowable instance has been created or not). My implementation is based on a couple of examples that I've seen on the web but which both fail because of this constructor problem.
erajoj
I tried your solution but still got AVs. I wonder is it because I raise an exception / abort during the initialization part of my code. I presume your tested involved creating 1000 objects AFTER the program had loaded.
If you create one instance of the object in the initialization part of the unit and destroy it in the finalization part.
If your unit is used by any unit in the project. Your object will get instantiated and is globally availably.
If you want to prevent others from instantiating your object put the declaration of your object in the implementation sections. That way opnly the unit the object is declared in can instantiate the object. And you do it once so there is no need for any constaints in the constructor.
The only problem you face then is that other developers can't derive any objects from your class. Since it is not in the interface section.
Regards Jacco
Let me know if this solves your problem or that you want to derive objects from TSpecial in other units.
If your unit is used by any unit in the project. Your object will get instantiated and is globally availably.
If you want to prevent others from instantiating your object put the declaration of your object in the implementation sections. That way opnly the unit the object is declared in can instantiate the object. And you do it once so there is no need for any constaints in the constructor.
The only problem you face then is that other developers can't derive any objects from your class. Since it is not in the interface section.
Regards Jacco
Let me know if this solves your problem or that you want to derive objects from TSpecial in other units.
ASKER
Deriving other objects isn't so much of an issue but if I declare TSpecial it in the 'implementation' section of a unit then surely no other units will be able to access any of its methods, not just the constructor. Other units / objects need to be able to access the functionality of the single global instance of TSpecial but not create other instances.
Regards
A
Regards
A
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Hi,
When an unhandled exception is raised in the constructor Delphi automatically returns nil and reraises the exception. So we should create pseudo exception. Try the following object
// turn optimization off for compiler not to remove our INTENDED exception
{$O-}
constructor TDummyObject.Create(DoIFai l: boolean);
begin
if DoIFail then
doifail := Boolean(PByte(0)^);//we can not read address 0 so exception
end;
{$O-}
destructor TDummyObject.Destroy;
begin
inherited;
end;
//this is test routine
procedure TForm1.Button4Click(Sender : TObject);
var
T : TDummyObject;
begin
//create and object without failinf
T := TDummyObject.Create(false) ;
ShowMessage(IntToStr(Integ er(T)));
T.Free; T := nil;
try
//create an object that fails
T := TDummyObject.Create(true);
//if not surrounded with try ... except we see the exception dialog so...
except
end;
ShowMessage(IntToStr(Integ er(T)));// you should see 0 in the box
end;
Regards,
Igor
When an unhandled exception is raised in the constructor Delphi automatically returns nil and reraises the exception. So we should create pseudo exception. Try the following object
// turn optimization off for compiler not to remove our INTENDED exception
{$O-}
constructor TDummyObject.Create(DoIFai
begin
if DoIFail then
doifail := Boolean(PByte(0)^);//we can not read address 0 so exception
end;
{$O-}
destructor TDummyObject.Destroy;
begin
inherited;
end;
//this is test routine
procedure TForm1.Button4Click(Sender
var
T : TDummyObject;
begin
//create and object without failinf
T := TDummyObject.Create(false)
ShowMessage(IntToStr(Integ
T.Free; T := nil;
try
//create an object that fails
T := TDummyObject.Create(true);
//if not surrounded with try ... except we see the exception dialog so...
except
end;
ShowMessage(IntToStr(Integ
end;
Regards,
Igor
Actually Igor...it's not the constructor that is the problem...
You can raise an exception in the constructor with no problem...the problem is where you raise an exception...You can not let an exception slip past an initialization...If there is a posibility that you will raise an exceiption in the initialization you should handle it and swallow it...
Rick
You can raise an exception in the constructor with no problem...the problem is where you raise an exception...You can not let an exception slip past an initialization...If there is a posibility that you will raise an exceiption in the initialization you should handle it and swallow it...
Rick
unit rpSingleton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
EInstance = class(Exception)
end;
TSingleton = class(TObject)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
// class function Instance: TSingleton;
class function Instance: TSingleton;
constructor create;
published
{ Published declarations }
end;
implementation
var
FSingleton: TSingleton;//no one can see this...except this unit...same as private
constructor TSingleton.Create;
begin
if Assigned(FSingleton) then
raise EInstance.Create('Only one instace can be create')
else begin
inherited Create;
FSingleton := Self;
end;
end;
class function TSingleton.Instance: TSingleton;
begin
if Assigned(FSingleton) then
Result := FSingleton
else begin
FSingleton := TSingleton.Create;
Result := FSingleton;
end;
end;
end.
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
EInstance = class(Exception)
end;
TSingleton = class(TObject)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
// class function Instance: TSingleton;
class function Instance: TSingleton;
constructor create;
published
{ Published declarations }
end;
implementation
var
FSingleton: TSingleton;//no one can see this...except this unit...same as private
constructor TSingleton.Create;
begin
if Assigned(FSingleton) then
raise EInstance.Create('Only one instace can be create')
else begin
inherited Create;
FSingleton := Self;
end;
end;
class function TSingleton.Instance: TSingleton;
begin
if Assigned(FSingleton) then
Result := FSingleton
else begin
FSingleton := TSingleton.Create;
Result := FSingleton;
end;
end;
end.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, rpSingleton;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Singleton: TSingleton;
MySingleton : TSingleton;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender : TObject);
begin
if MySingleton = Singleton then
Showmessage('same singleton')
else
ShowMessage('different singleton');
end;
procedure TForm1.Button2Click(Sender : TObject);
begin
MySingleton := TSingleton.create;
end;
procedure TForm1.Button3Click(Sender : TObject);
begin
MySingleton := Tsingleton.instance;
end;
initialization
try
Singleton := TSingleton.create;
MySingleton := TSingleton.create;
except
//we swallow the exception raised by MySingleton...
//otherwise runtime error 217...GPF
end;
end.
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, rpSingleton;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Singleton: TSingleton;
MySingleton : TSingleton;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender
begin
if MySingleton = Singleton then
Showmessage('same singleton')
else
ShowMessage('different singleton');
end;
procedure TForm1.Button2Click(Sender
begin
MySingleton := TSingleton.create;
end;
procedure TForm1.Button3Click(Sender
begin
MySingleton := Tsingleton.instance;
end;
initialization
try
Singleton := TSingleton.create;
MySingleton := TSingleton.create;
except
//we swallow the exception raised by MySingleton...
//otherwise runtime error 217...GPF
end;
end.
I think this is the safest method...
EInstance = class(Exception)
end;
TSingleton = class(TObject)
protected
constructor init;
public
class function Instance: TSingleton;
constructor create;
end;
implementation
var
FSingleton: TSingleton;(*no one can see this...except this unit...same as private*)
constructor TSingleton.Create;
begin
raise EInstance.Create('Use the Instance method');
end;
constructor TSingleton.Init;
begin
inherited create;
end;
class function TSingleton.Instance: TSingleton;
begin
if Assigned(FSingleton) then
Result := FSingleton
else begin
FSingleton := TSingleton.Init;
Result := FSingleton;
end;
end;
EInstance = class(Exception)
end;
TSingleton = class(TObject)
protected
constructor init;
public
class function Instance: TSingleton;
constructor create;
end;
implementation
var
FSingleton: TSingleton;(*no one can see this...except this unit...same as private*)
constructor TSingleton.Create;
begin
raise EInstance.Create('Use the Instance method');
end;
constructor TSingleton.Init;
begin
inherited create;
end;
class function TSingleton.Instance: TSingleton;
begin
if Assigned(FSingleton) then
Result := FSingleton
else begin
FSingleton := TSingleton.Init;
Result := FSingleton;
end;
end;
ASKER
Thanks everybody for all your suggestions so far. I'll reply again when I've had a chance to test them out.
Thanks again
Andrew
Thanks again
Andrew
ASKER
Its taken a while but I think I've finally cracked this one based on ideas that have been posted here and other ideas I've picked up from newsgroups. The best solution I've seen so far is this one:
unit Singleton;
interface
uses SysUitls, Classes;
type
TSingleton = class(TObject)
protected
// Attributes.
FRefCount : integer;
// Methods.
constructor CreateNew;
procedure IncRefCount;
procedure DecRefCount;
public
class function Create: TSingleton;
prodedure Destroy;
end;
implementation
const
uSingleton: TSingleton = nil;
// TSingleton Protected Methods.
constructor TSingleton.CreateNew;
begin
inherited Create;
end;
procedure TSingleton.IncRefCount;
begin
Inc(FRefCount);
end;
procedure TSingleton.DecRefCount;
begin
Dec(FRefCount);
end;
// TSingleton Public Methods.
class function TSingleton.Create: TSingleton;
begin
if uSingleton = nil then
uSingleton := TSingleton.CreateNew;
uSingleton.IncRefCount;
result := uSingleton;
end;
prodedure TSingleton.Destroy;
begin
DecRefCount;
if FRefCount = 0 then begin
inherited Destory;
uSingleton := nil;
end;
end;
end.
The beauty of this design is that it hides the real constructor from other classes and doesn't rely on exceptions which seemed to be the source of a lot of problems. Thanks again for all your help.
unit Singleton;
interface
uses SysUitls, Classes;
type
TSingleton = class(TObject)
protected
// Attributes.
FRefCount : integer;
// Methods.
constructor CreateNew;
procedure IncRefCount;
procedure DecRefCount;
public
class function Create: TSingleton;
prodedure Destroy;
end;
implementation
const
uSingleton: TSingleton = nil;
// TSingleton Protected Methods.
constructor TSingleton.CreateNew;
begin
inherited Create;
end;
procedure TSingleton.IncRefCount;
begin
Inc(FRefCount);
end;
procedure TSingleton.DecRefCount;
begin
Dec(FRefCount);
end;
// TSingleton Public Methods.
class function TSingleton.Create: TSingleton;
begin
if uSingleton = nil then
uSingleton := TSingleton.CreateNew;
uSingleton.IncRefCount;
result := uSingleton;
end;
prodedure TSingleton.Destroy;
begin
DecRefCount;
if FRefCount = 0 then begin
inherited Destory;
uSingleton := nil;
end;
end;
end.
The beauty of this design is that it hides the real constructor from other classes and doesn't rely on exceptions which seemed to be the source of a lot of problems. Thanks again for all your help.
I like what you have Andrew...
The only thing I would add would be...
Destructor TSingleton.DestroyInstance ;
begin
inherited Destroy;
uSingleton := nil;
end;
function Instance: TSingleton;
begin
result := uSingleton;
end;
The DestroyInstance would allow the programmer to make sure the Singleton was destroyed when he closed the program.
The Instance would allow the program to return nil if the Singleton has not been created or destroyed.
I like the class function of Create...didn't think of it...very cute...
Rick
The only thing I would add would be...
Destructor TSingleton.DestroyInstance
begin
inherited Destroy;
uSingleton := nil;
end;
function Instance: TSingleton;
begin
result := uSingleton;
end;
The DestroyInstance would allow the programmer to make sure the Singleton was destroyed when he closed the program.
The Instance would allow the program to return nil if the Singleton has not been created or destroyed.
I like the class function of Create...didn't think of it...very cute...
Rick
constructor Create;override;