I'm creating a non-visual component. Short Code:
// Element of some list item
TMyItem = class
private
Id: integer;
Caption: string;
public
constructor Create(const aId: integer; const aCaption: string);
end;
// List of items
TMyItemList = class(TObjectList<TMyItem>)
public
constructor Create;
end;
// The component
TMyComp = class(TComponent)
private
FMyList: TMyItemList;
public
constructor Create;
implementation
constructor TMyComp.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FMyList:= TMyItemList.Create;
end;
Question: why "FMyList" not exists when "MyComp" created? Assigned(FMyList) = false ...
This is not your actual code. Your actual code compiles. But we can guess what the problem is. Here:
type
TMyComp = class(TComponent)
private
FMyList: TMyItemList;
public
constructor Create;
end;
The constructor for TComponent is declared like this:
constructor Create(AOwner: TComponent); virtual;
It's a virtual constructor because the Delphi streaming framework relies upon metaclasses to create objects. If you want the framework to call your constructor you have to override this virtual constructor.
constructor Create(AOwner: TComponent); override;
I'm reasonably confident of this guess, but we should not need to guess. Next time, please submit real code, a Minimal, Complete, and Verifiable Example.
Related
This is blowing my mind... I just want to make a new IdIOHandler, and, as usual, I need to do some initialization in the constructor... Normally, I override the constructor of the base class, but this TIdIOHandlerStack which I inherit from, is far from "normal" ! It has no constructor to override, and is not known (to me) how it is created.
TIdEnhancedIOHandler = class(TIdIOHandlerStack)
private
FSendBuffer: TIdBytes;
FSendBuff: TDataStream;
public
constructor Create; // <-- I tested all the variations here, but non of them work
destructor Destroy; override;
end;
implementation
constructor TIdEnhancedIOHandler.Create;
begin
inherited Create;
FSendBuff:= TDataStream.Create(#SendBuffer);
end;
destructor TIdEnhancedIOHandler.Destroy;
begin
FSendBuff.Free;
inherited;
end;
initialization
TIdEnhancedIOHandler.SetDefaultClass;
Where should I put my intitialization code so that it is executed when a new instance of TIdEnhancedIOHandler is created BY DEFAULT in all Indy Components which use IOHandlers ?
I found it... It was the InitComponent method that I must override.
TIdEnhancedIOHandler = class(TIdIOHandlerStack)
private
FSendBuffer: TIdBytes;
FSendBuff: TDataStream;
public
procedure InitComponent; override;
destructor Destroy; override;
end;
implementation
procedure TIdEnhancedIOHandler.InitComponent;
begin
inherited;
FSendBuff:= TDataStream.Create(#SendBuffer);
end;
I read this answer about constructors and its directives (reintroduce, overload, virtual, override, etc.) but I can't reach the goal I want. Check the following pseudo-code (I mean the code without any directive yet):
TBaseClass = class
constructor Create;
end;
TStringStuff = class (TBaseClass)
constructor Create(str: string);
end;
TNumberStuff = class (TBaseClass)
constructor Create(num: integer);
constructor Create(num: decimal);
end;
I want a TStringStuff object can be created using its own constructor and the parent one:
var
StrStuff: TStringStuff;
begin
StrStuff:=TStringStuff.Create();
//or
StrStuff:=TStringStuff.Create('bla');
end;
but I also want a TNumberStuff object can be created using ONLY its own constructors, i.e. if someone use my library he wont be able to create a TNumberStuff without parameter:
var
NumStuff: TNumberStuff ;
begin
NumStuff:=TNumberStuff.Create(10);
//or
NumStuff:=TNumberStuff.Create(10.5);
// but NOT: NumStuff:=TNumberStuff.Create();
end;
So how to use the directives to achieve my goals?
(I am using Delphi 10.2 Tokyo)
For a variety of reasons you can't achieve exactly what you want, but this is as close as you can get.
I have introduced a 'superbase' class if you like with a hidden constructor, only visible in that unit. The only function of TBaseClass now is to 'expose' the constructor so that you can create instances of TBaseClass.
unit Test1;
interface
type
TBaseBaseClass = class
// This does all the work of TBaseClass, but hides the contructor
private
constructor Create; reintroduce; // this can only be accessed within this unit
end;
TBaseClass = class(TBaseBaseClass)
// a creatable class. No actual work is done here. It's only purpose is to
// 'expose' the base constructor
public
constructor Create; reintroduce;
end;
TStringStuff = class( TBaseBaseClass )
public
constructor Create; reintroduce; overload;
constructor Create( str : string ); reintroduce; overload;
end;
TNumStuff = class( TBaseBaseClass )
public
constructor Create( num : integer ); reintroduce; overload;
constructor Create( num : single ); reintroduce; overload;
end;
implementation
{ TStringStuff }
constructor TStringStuff.Create(str: string);
begin
inherited Create;
// ...
// other stuff
end;
constructor TStringStuff.Create;
begin
inherited Create;
// does no extra work! 'exposes' TBaseBaseClass constructor
// but required because of rules of polymorphism
end;
{ TBaseBaseClass }
constructor TBaseBaseClass.Create;
begin
inherited Create;
// ...
// other stuff - does the work originally in TBaseClass
end;
{ TBaseClass }
constructor TBaseClass.Create;
begin
inherited Create;
// does no extra work! 'exposes' TBaseBaseClass constructor
end;
{ TNumStuff }
constructor TNumStuff.Create(num: single);
begin
inherited Create;
// ...
// other stuff
end;
constructor TNumStuff.Create(num: integer);
begin
inherited Create;
// ...
// other stuff
end;
end.
In another unit, if you put a test procedure like this
procedure Test;
var
iBaseClass : TBaseClass;
iStringStuff : TStringStuff;
iNumStuff : TNumStuff;
begin
iBaseClass := TBaseClass.Create;
iStringStuff := TStringStuff.Create;
iNumStuff := TNumStuff.Create;
end;
you will find it does not compile.
But there are a couple of 'gotchas'. If you try putting this procedure in the same unit as the original definitions it will compile. That is because the TBaseBase constructor is visible within the unit.
The second gotcha is related to the fact that the hidden constructor is parameterless, and there is a public parameterless constructor for TObject, from which all objects are descended. So if you try to create an instance of TBaseBaseClass using a constructor without parameters it will compile. It just won't use the constructor you might expect. It will use the TObject constructor.
Finally I would advise against ever trying to hamstring other programmers. By all means lead them in the right direction, but don't try and stop them doing what they want to do. With that in mind, I would not do it this way. I would make the TBaseBase constructor protected, not private. I am just showing this to answer your question.
Here is how I wold deal with your problem
First I would set BaseClass constructor to be virtual and thus allow overriding it in desendant classes
TBaseClass = class
constructor Create; virtual;
end;
Then in TStringStuff class I would change your existing constructor so that its string parameter is actually an optional parameter. This would allow you to call this constructor with or without string parameter passed to it. So now ony thing you need to do is call parent constructor with the help of inherited when no parameter was passed to consturctor or do necessary work before if string paramter was passed to the constructor.
TStringStuff = class (TBaseClass)
constructor Create(str: string = ''); override;
end;
And in TNumberStuff class you just reintroduce your overloaded constructors to be able to manage multiple posible input parameter types that can be passed to the constructor.
TNumberStuff = class (TBaseClass)
constructor Create(num: integer); reintroduce; overload;
constructor Create(num: decimal); reintorduce; overload;
end;
Take a look at this class:
TTest = class(TObject)
public
constructor Create(A:Integer);overload;
constructor Create(A,B:Integer);overload;
end;
Now when we want to use the class:
var
test: TTest;
begin
test:= TTest.Create; //this constructor is still visible and usable!
end;
Can anyone help me with hiding this constructor?
So long as you have overloaded constructors named Create, you cannot hide the parameterless TObject constructor when deriving from TObject.
This is discussed here: http://www.yanniel.info/2011/08/hide-tobject-create-constructor-delphi.html
If you are prepared to put another class between your class and TObject you can use Andy Hausladen's trick:
TNoParameterlessContructorObject = class(TObject)
strict private
constructor Create;
end;
TTest = class(TNoParameterlessContructorObject)
public
constructor Create(A:Integer);overload;
constructor Create(A,B:Integer);overload;
end;
You can hide the inherited Create by just introducing a non overloaded Create. As you need two overloaded Create, you can either merge those into one Create with an optional second parameter:
TTest = class(TObject)
public
constructor Create(A:Integer; B: Integer = 0);
end;
This will give a compiler warning, signalling that you're hiding the default parameterless constructor. To get rid of the warning you can declare the hiding constructor like so:
TTest = class(TObject)
public
constructor Create(A:Integer; B: Integer = 0); reintroduce;
end;
or, if this is not feasible, you can introduce an intermediate class introducing the first create and then the final class with the overloaded second one:
preTest = class(TObject)
public
constructor Create(A:Integer); reintroduce;
end;
TTest = class(preTest)
public
constructor Create(A,B:Integer);overload;
end;
Another option is to use the deprecated keyword and raise an exception at runtime.
TTest = class(TObject)
public
constructor Create; overload; deprecated 'Parameterless constructor is not Supported for a TTest class';
constructor Create(const A: Integer); overload;
constructor Create(const A, B: Integer); overload;
end;
implementation
constructor TTest.Create;
begin
raise Exception.Create('Parameterless constructor is not Supported for a TTest class.');
end;
Through the two inheritance, user creation of TMySingleton class can be prevented from design time rather than runtime.
unit MySingleton;
interface
uses System.Classes, System.SysUtils;
type
// Constructor Block external access
THideConstructor = class abstract
strict protected
constructor Create; virtual; abstract;
end;
// Switching the access to the Create function THideConstructor in TObject through the constructor Overloading
// Declaring Create Method as a procedure to prevent class call-TMySingle.Create('string') call impossible
TOverloadConstructor = class(THideConstructor)
public
procedure Create(s: string); reintroduce; overload; deprecated 'null method';
end;
TMySingleton = class sealed(TOverloadConstructor)
private
class var MyObj: TMySingleton;
strict protected
// Hiding TOverloadConstructor.Create(s: string);
// Implement THideConstructor.Create
constructor Create; override;
public
class function Obj: TMySingleton;
function Echo(const value: string): String;
destructor Destroy; override;
end;
implementation
{ TMySingleton }
constructor TMySingleton.Create;
begin
// TODO
end;
destructor TMySingleton.Destroy;
begin
Self.MyObj := nil;
inherited;
end;
function TMySingleton.Echo(const value: string): String;
begin
result := value;
end;
class function TMySingleton.Obj: TMySingleton;
begin
if MyObj = nil then
MyObj := Self.Create;
result := MyObj;
end;
{ TOverloadContructor }
procedure TOverloadConstructor.Create(s: string);
begin
// null method
end;
initialization
TMySingleton.MyObj := nil;
finalization
if Assigned(TMySingleton.MyObj) then
FreeAndNil(TMySingleton.MyObj);
end.
If the user
var
Singleton: TMySingleton;
begin
Singleton := TMySingleton.Create;
Design-time error occurs.
[dcc32 Error] Unit1.pas(33): E2625 Private member 'THideConstructor.Create' is inaccessible here MySingleton.pas(11): Related method: constructor Create;
enter image description here
Also, you can't see any autocomplete hints named Create.
I have a class which inherited from TCollection (lets call it "TMyCollection") and I have to inherit a new class from it (lets call it "TMyItems")
Usually we pass the ItemClass type in the constructor of the TCollection but in my case the constructor of TMyCollection is overrided with new constructor which doesn't take ItemClass and it does only take the Owner.
I need to know how to change the ItemClass in "TMyItems" if the inherited constructor doesn't accept ItemClass parameter.
Regards.
You can still call the inherited TCollection.Create from within a subclass, even if it doesn't have the same signature:
TMyCollectionItem = class(TCollectionItem)
private
FIntProp: Integer;
procedure SetIntProp(const Value: Integer);
public
property IntProp: Integer read FIntProp write SetIntProp;
end;
TMyCollection = class(TCollection)
public
constructor Create(AOwner: TComponent);virtual;
end;
{ TMyCollection }
constructor TMyCollection.Create(AOwner: TComponent);
begin
inherited Create(TMyCollectionItem); // call inherited constructor
end;
EDIT :
As per the original poster's comments, a "trick" is to mark the new constructor as overloaded. As it is an overload, it doesn't hide access to the TCollection constructor.
TMyCollection = class(TCollection)
public
constructor Create(AOwner: TComponent);overload; virtual;
end;
TMyItem = class(TCollectionItem)
private
FInt: Integer;
public
property Int: Integer read FInt;
end;
TMyItems = class(TMyCollection)
public
constructor Create(AOwner: TComponent);override;
end;
implementation
{ TMyCollection }
constructor TMyCollection.Create(AOwner: TComponent);
begin
inherited Create(TCollectionItem);
end;
{ TMyItems }
constructor TMyItems.Create(AOwner: TComponent);
begin
inherited Create(TMyItem);
inherited Create(AOwner); // odd, but valid
end;
end.
Update: gutted the question with a simpler example, that isn't answered
by the originally accepted answer
Given the following class, and its ancestor:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Right now TCellPhone has 3 constructors visible:
Cup: Integer
Cup: Integer; Teapot: string
Teapot: string = ''
What do i do to TCellPhone so that the ancestor constructor (Teapot: string = '') is not visible, leaving only the declared constructors:
Cup: Integer
Cup: Integer; Teapot: string
Note: Usually the simple act of having a descendant constructor hides the ancestor:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); virtual;
end;
Cup: Integer
And if you wanted to keep both the
ancestor constructor and the
descendant, you would mark the
descendant as an overload:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
end;
Cup: Integer
Teapot: string = ''
In this question's example code, Delphi is mistaking my overload keywords:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
to think that:
i want to overload my constructors with the ancestor,
when really i want to overload it with the sibling
How do i hide the ancestor constructor?
Note: It might be impossible to hide the ancestor, non-virtual, constructor using the Delphi language as it is currently defined. "Not possible" is a valid answer.
Attempted Answer (failed)
i tried marking the descendant constructors with reintroduce (falling back to my mode of randomly adding keywords until it works):
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); reintroduce; overload; virtual;
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
But that didn't work, all three constructors are still visible. :(
Original Question
i have an object that descends from a class that has constructors don't want to see:
TEniac = class(TObject)
constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create
TComputer = class(TEniac) ...
constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create(nil)
TCellPhone = class(TComputer)
constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
TiPhone = class(TCellPhone)
constructor Create(sim: TSimChip); //calls inherited Create(sim, 0)
Note: This is a hypothetical example. As in the real world, the ancestor objects cannot be changed without breaking existing code.
Now when someone's using TiPhone i don't want them even being able to see the constructor from TEniac:
iphone := TiPhone.Create(powerCord);
Worse still: if they call that constructor, they completely miss my constructor, and everything done in between. It's pretty easy to call the wrong constructor, all of them are visible in the IDE code-completion, and will compile:
TiPhone.Create;
and they get a completely invalid object.
i could change TCellPhone to throw an exception in those constructors:
TCellPhone.Create(PowerCord: TPowercord)
begin
raise Exception.Create('Don''t use.');
end;
But developers won't realize they're calling the wrong constructor until the customer finds the error one day and fines us bazillions of dollars. In fact, i'm trying to find everywhere i call the wrong constructor - but i can't figure out how to make Delphi tell me!
If I remember correctly, then reintroduce should help for virtual methods.
The reintroduce directive suppresses compiler warnings about hiding previously declared virtual methods.
Use reintroduce when you want to hide an inherited virtual method with a new one.
To answer your updated question - I think it's not possbile to hide a non-virtual constructor with overloading in a directly derived class, but I tried the following successfully:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TIndermediateComputer = class(TComputer)
protected
// hide the constructor
constructor Create;
end;
TCellPhone = class(TIndermediateComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
It's impossible to ever make a constructors introduced in an ancestor inaccessible for the creation of a derived class in Delphi because you can always do this:
type
TComputerClass = class of TComputer;
var
CellPhoneClass: TComputerClass = TCellPhone;
CellPhone : TCellPhone;
begin
CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;
Nothing you could do in the code of any derived class would ever be able to prevent anyone from calling the TComputer.Create constructor for creating an instance of the derived class.
The best you could do is:
TComputer = class(TObject)
public
constructor Create(Teapot: string=''); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Teapot: string=''); overload; override;
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
In that case the code above would at least be calling TCellPhone.Create(Teapot: string='') instead of TComputer.Create(Teapot: string='')
Instead of only raising an "Don't use" exception in the overridden invalid constructors, consider marking them deprecated in the class where they become invalid. That should produce nice compiler warnings when these invalid constructors are used erroneosly.
TCellPhone = class(TComputer)
constructor Create(PowerCord: TPowerCord=nil); deprecated;
constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
In addition, use override or reintroduce as needed.
You can't hide the parent class' constructor unless it was declared virtual or dynamic. You can however prevent it from being called from the child class. Consider your example:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
TComputer.Create will always be visible from TCellPhone. You can prevent TComputer.Create from being inadvertantly called by declaring a TCellPhone.Create with the same signature.
TCellPhone = class(TComputer)
public
constructor Create(Teapot: string='');
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Then as long as you don't have a call to inherited in the body of TCellPhone.Create(Teapot: string='') you can prevent TComputer.Create from being called in TCellPhone and its descendants. The following:
TCellphone.Create;
TCellphone.Create('MyPhone');
Will resolve to TCellPhone's implementation.
Additionally:
TiPhone = class(TCellPhone)
constructor Create;
end;
constructor TiPhone.Create;
begin
inherited;
end;
Will invoke TCellPhone.Create and not TComputer.Create.
You want to reintroduce the constructor:
TiPhone = class(TCellPhone)
constructor Create(sim: TSimChip); reintroduce;
See TComponent.Create in the Delphi source code for a real-world example of this.
I know that this is 5 years old topic, but still it may help someone.
The only way to hide the ancestor's constructor is to rename one of the two Create methods to something else and remove the need of overload directive.
It looks weird but it's the only way. At least in older versions of Delphi. I don't know is it possible now in the XE xxx versions.