I have a class that descents from TObject, if I do not call the parent method, is it bad? I ask because when looking at TObject.Create/Destroy, they do nothing.
That's why we override/expose them ourselves.
There's no code example, I just want to make sure.
The most common behaviour is to call inherited. So if I explicitly do not want the inherited behaviour, I do not call inherited. Otherwise, I do. If the inherited behaviour is a no-op like TObject.Create/Destroy, I still call inherited.
Note also that the situtation for constructors and destructors is perhaps a little different than for other methods. It's exceptionally rare that you would need to skip a call to the inherited method. I cannot think of an example. Invariably constructors are creating and destroying other objects, so how could you contemplate skipping that?
I know that some authors write code that omits inherited when deriving directly from TObject, because they know that the implementation in TObject does nothing. I do not like that and to me it is a mistake to do that. The implementation details of TObject should not be leaking into derived classes.
I'm pretty sure that TObject.Create/Destroy will always be no-ops. If Embarcadero changed that then so much code would break. But what about one of your classes. Suppose you have a class derived from TObject. And then you have another class derived from that:
TMyClass1 = class
....
end;
TMyClass2 = class(TMyClass)
....
end;
You have no constructor for TMyClass1 and a constructor for TMyClass2 that looks like this:
constructor TMyClass2.Create;
begin
// no need to call inherited, it's a no-op
FMyObj := TBlahBlah.Create;
end;
Then one day you modify the TMyClass1 constructor to do something. Now TClass2 is broken. So, I would never ever omit a call to inherited because that call does nothing.
The situation for normal instance methods is a little different. It is more plausible that you want to ignore the base class implementation and provide a brand new implementation. But take the decision based on what the derived class wants to do rather than whether or not the super class has an empty implementation for the method.
You only need to call inherited if you want the code of the ancestors to execute. Sometimtes you need that code, sometimes you do not. You can also use conditionals and call inherited only if some condition is fulfilled, and you can choose when to do it (beginning of method, end of method, ...).
Hence, it all depends on the situation.
Here's an example.
Even though TObject's Create/Destroy methods don't do anything in the current version, they might in some future version (unlikely, but still might). So is it a MUST for you right now? No, because it won't accomplish anything, but only because the current parent doesn't add any functionality.
If you want your descendant objects to always add functionality to their parents, it would probably be a good idea to consistently call inherited.
Related
I am having some difficulty understanding typecasting when using a class that is a passed parameter. I tried searching for this but couldn't find other answers.
I am working with some legacy Delphi code, using Delphi 2006, which doesn't support Generics (introduced in Delphi 2009).
The code is using TLists to store pointers to instantiated classes of particular types. When clearing the list, they use this:
procedure ClearList(AList: TList);
var i: Integer;
begin
for i := 0 to AList.Count - 1 do
TObject(AList[i]).Free;
AList.Clear;
end;
And it is called like this:
ClearList(FExtraVisitTypes);
ClearList(FDiagnoses);
ClearList(FProcedures);
ClearList(FImmunizations);
ClearList(FSkinTests);
ClearList(FPatientEds);
ClearList(FHealthFactors);
ClearList(FExams);
My understanding of this may be off, but I am concerned that if the pointed-to objects are freed as TObject, that the destructor of the descendant object won't be called, potentially leading to a memory leak. (My polymorphisim kung-fu is a bit rusty, which may be causing my confusion.)
So I tried to change the clear function as below:
procedure ClearList(AList: TList; ItemClass: TPCEItemClass); //mod to add ItemClass
var i: Integer;
begin
for i := 0 to AList.Count - 1 do begin
(AList[i] as ItemClass).Free;
end;
AList.Clear;
end;
TPCEItemClass is defined like this:
TPCEItemClass = class of TPCEItem;
I then changed the clear calls like this:
ClearList(FExtraVisitTypes, TPCEProc);
ClearList(FDiagnoses, TPCEDiag);
ClearList(FProcedures, TPCEProc);
ClearList(FImmunizations, TPCEImm);
ClearList(FSkinTests, TPCESkin);
ClearList(FPatientEds, TPCEPat);
ClearList(FHealthFactors, TPCEHealth);
ClearList(FExams, TPCEExams);
But the compiler won't allow this and gives this error:
[Pascal Error] uPCE.pas(1730): E2015 Operator not applicable to this operand type
For this erroneous line:
(AList[i] as ItemClass).Free;
Questions:
Does the original way of coding, where the item is freed by simply calling the great-great-great (etc) ancestor Free method end up effecting the descendant's destructor method? As I write this, I'm now thinking that it actually does. But I don't know why. So any answers to help me keep this in my head would be great.
Why does my method of trying to typecast via the parameter which is of type class not work? Is this just not allowed? Or is my syntax wrong? Is there another way to do this?
Am I going about this all wrong? Is there a better way?
Thanks
I am concerned that if the pointed-to objects are freed as TObject, that the destructor of the descendant object won't be called, potentially leading to a memory leak.
That is not the case for classes that are properly implemented.
All classes derive from TObject. TObject.Free() calls the TObject.Destroy() destructor, which is virtual. Any descendant that requires destruction logic must override that destructor (if it doesn't, it has a flaw that needs fixing).
So, in properly written code, the original code will work perfectly fine as shown. Calling Free() on any valid and correctly implemented object will invoke its most-derived destructor.
Now, that being said, there have been plenty of cases over the years of people forgetting to override the destructor when their classes require it, thus causing the kinds of memory leaks you are worried about. So, make sure you pay attention to what your classes are doing, and you will be fine.
So I tried to change the clear function as below ... But the compiler won't allow this and gives this error
Correct, because you can't perform a type-cast on an object using a variable to a metaclass type, like you are trying to do. Type-casts require the target type to be specified at compile-time, but metaclass variables are not assigned until runtime.
Does the original way of coding, where the item is freed by simply calling the great-great-great (etc) ancestor Free method end up effecting the descendant's destructor method?
The original code will work just fine 99% of the time, yes. Most Delphi coders are good about override'ing the destructor when it is appropriate. But that other 1% is only when you are dealing with classes that are not implemented correctly, in which case it is their author's responsibility to fix them, not your responsibility to fix the code that is calling Free() on them.
As I write this, I'm now thinking that it actually does. But I don't know why.
Polymorphic dispatch of the virtual destructor, just like when calling any other virtual method.
Why does my method of trying to typecast via the parameter which is of type class not work? Is this just not allowed?
Correct. It is illegal.
Is there another way to do this?
No (well, yes, but it involves walking an object's class structure's manually at runtime, but that requires a deep understanding of how the compiler lays out objects in memory, so I'm not going to get into that here).
Is it ok to initialize members field before calling inherited in create ?
IE:
constructor TMyObject.create(AOwner: TComponent);
begin
fMyField := xxx;
inherited
end;
instead of normal way :
constructor TMyObject.create(AOwner: TComponent);
begin
inherited
fMyField := xxx;
end;
just to know is their is any drawback i didn't see ...
When an instance of a class is instantiated, the memory is allocated and default initialized (e.g. filled with zeros), and then the constructor is called. So, any code in a constructor executes after the default initialization, which would be the one timing issue that you might imagine scuppering what you are doing.
However, code such as yours is usually indicative of a deeper design malaise. How could it matter whether you initialized a value before calling the inherited constructor? There are two reasons I can imagine where you might be tempted to do this:
If the field in question is declared in your derived class, then the only way the ancestor code could access it is by calling a virtual (or dynamic) method. And doing so in a constructor is dangerous because the object is only partially created. That's a big toxic code smell.
If the field in question is declared in the ancestor classes, you might be using this mechanism to in effect pass an argument from derived class to ancestor. That's a rather weird way of doing it. A much more appropriate way would be to use arguments in your constructor.
Let's say I have two classes in Unit1: TParent = class(TCustomControl) and TDescendant = class(TParent) and they both have a lot of methods, fields and properties.
In Unit2 I need to modify TParent. Let's say I only need to modify a single method, Method1, in a very simple way that doesn't really affect anything outside itself (say, print a text in red color instead of black). I also need TDescendant to be affected by this modification (so every time it calls Method1 the modified Method1 is executed).
Is there a way to do that in Delphi 7, without modifying Unit1 and without copying the entire TDescendant class into Unit2?
On top of that I really need the class names to remain the same (I'm simply trying to modify a method in a 3rd party control without creating a whole new control to do it).
I made an interceptor class of TParent in Unit2, but I don't know how (if at all possible) to "tell" TDescendant to become a descendant of the interceptor class instead of the original.
As far as I know this is not possible without some serious low level hacks (pointer/memory based). Therefor I would not suggest is at all... (also I don't know the actual answer to that solution).
When you only want to add some extra methods to the TParent class, you can use helper classes (Delphi XE+ - maybe even earlier versions).
Ex.
TParentHelper = class helper for TParent
public
procedure MyMethod(param: string);
end;
Now you can dos stuff like:
uses
MyParentHelperU;
Procedure test(D: TDescendant);
begin
D.MyMethod('test');
end;
Notes:
you need to include the unit in which the helper class in defined, in order to use this functionality.
You can only add methods to the class (and descendants), for which the helper class has been assigned. You cannot add properties or fields.
Protected properties can be accessed by the helperclass, but are not always shown by the IDE
what different between InstanceClass.NewInstance+Instance.Create and InstanceClass.Create;
Method1:
Instance := TComponent(InstanceClass.NewInstance);
Instance.Create(Self);
Method2:
Instance := InstanceClass.Create(Self);
Which is better?
I would always use InstanceClass.Create if it is appropriate – and invariably it is.
There are plenty of reasons. A very good one is that the single line version is more concise. Another is that the single line version is the standard, commonly used approach.
Yet another reason is the handling of exceptions in the constructor which your method 1 does not manage correctly. In case of an exception, the new instance will be destroyed, but the instance variable has still been assigned to. That's an important difference from method 2 and goes against all the lifetime management conventions of Delphi.
You mention TApplication.CreateForm. Let's take a look at it:
Instance := TComponent(InstanceClass.NewInstance);
TComponent(Reference) := Instance;
try
Instance.Create(Self);
except
TComponent(Reference) := nil;
raise;
end;
Remember that Reference is the form variable that you pass as a var parameter. The point about this is this code assigns that form variable before calling the constructor. Normally that assignment is only made after the constructor completes.
Presumably this is so that code which references the form variable (often a global variable) can work even if it is invoked from inside that form's constructor. This is a very special case and is overwhelmingly the exception rather than the rule. Don't let this special case drive your mainstream coding style.
(I added this answer, because IMHO others where not complete)
Method 2 is the correct one.
Method 1 if never to be called, since there is an hidden parameter to the constructor call, which may fail proper iniatialize: in fact, NewInstance is a per-class pseudo virtual method!
In fact, there is an hidden boolean parameter at constructor call (register EDX, since EAX=class). As stated by official documentation:
Constructors and destructors use the same calling conventions as other
methods, except that an additional Boolean flag parameter is passed to
indicate the context of the constructor or destructor call.
A value of False in the flag parameter of a constructor call indicates that the constructor was invoked through an instance object
or using the inherited keyword. In this case, the constructor behaves
like an ordinary method. A value of True in the flag parameter of a
constructor call indicates that the constructor was invoked through a
class reference. In this case, the constructor creates an instance of
the class given by Self, and returns a reference to the newly created
object in EAX.
In particular, when called that way, the class won't call the _ClassCreate function. It may fail to initialize the class, if the class is not to be created with the default NewInstance function. In fact, this function is inserted into the class VMT: in some rare cases, it may be overloaded (e.g. to provide another memory allocation pattern - may be a garbage collector or some speed-optimized allocator). So calling directly InstanceClass.NewInstance can be buggy, in some border cases.
function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
...
TEST DL,DL
JL ##noAlloc
CALL dword ptr [EAX].vmtNewInstance
##noAlloc:
...
Therefore, calling InstanceClass.NewInstance directly is only to be done on purpose, only if you want to cancel two overridden vmtNewInstance / vmtFreeInstance (and in this case, you may have to also NOT call .Free / .Destroy, but you own memory free function). So: never call NewInstance, but the constructor, as designed and documented by Embarcadero (and FreePascal team by the way), unless you need to make some internal low-level tweak.
NEVER use method 1 to create an object! It may work 99.9 % of the time, but may fail some cases, or with a compiler/RTL enhancement in the future (like a garbage collector). Even if the VCL sometimes uses NewInstance, you should not use it - I would rather prefer this method to be made protected.
Second is better because it is a standard method to create a class instance, while procedural form should be used within constructor to call inherited constructor.
Does Delphi call inherited on overridden procedures if there is no explicit call in the code ie (inherited;), I have the following structure (from super to sub class)
TForm >> TBaseForm >> TAnyOtherForm
All the forms in the project will be derived from TBaseForm, as this will have all the standard set-up and destructive parts that are used for every form (security, validation ect).
TBaseForm has onCreate and onDestroy procedures with the code to do this, but if someone (ie me) forgot to add inherited to the onCreate on TAnyOtherForm would Delphi call it for me? I have found references on the web that say it is not required, but nowhere says if it gets called if it is omitted from the code.
Also if it does call inherited for me, when will it call it?
No, if you leave the call to inherited away, it will not be called. Otherwise it would not be possible to override a method and totally ommit the parent version of it.
It is worth mentioning that not calling inherited in Destroy of any object can cause memory leaks. There are tools available to check for this in your source code.
The inherited call has to be made explicitly. In general no language automatically calls the inherited function in equivalent situations (class constructors not included).
It is easy to forget to make the inherited call in a class constructor. In such a situation if a base class needs to initialize any data you have an access violation waiting to happen.
Perhaps you could override DoCreate and DoDestory in your TBaseForm class so you could ensure some code is executed regardless of the implementation of child classes.
// interface
TBaseForm = Class(TForm)
...
Protected
Procedure DoCreate(Sender : TObject); Override;
End
// implementation
Procedure TBaseForm.DoCreate(Sender : TObject);
Begin
// do work here
// let parent call the OnCreate property
Inherited DoCreate(Sender);
End;
Inherited must be explicitly called in descendant objects as well as in visual form inheritance. If you use class completion then it adds inherited automatically if you flagged the definition as override (but not for reintroduce). If you are using visual form inheritance then when you add a new event hander through the form editor then it will add inherited as well.
The inherited code is not called implicitly, as the others have indicated. You must call it explicitly. This gives you some useful flexibility. For instance, you might want to do some preprocessing code prior to the inherited code, then do some post processing code afterwards. This might look like:
procedure TMyCalcObject.SolveForX;
begin
ResetCalcState;
inherited SolveForX;
PostProcessSolveForX;
end;
You must call it explicitly. This allows a lot of flexibility, since you can choose at which point in code to call the inherited method. But it's also a big source of bugs. It's easy to forget to call the inherited function and compiler has no way to tell if you did it deliberately or you just forgot.
There should be some kind of directive "skip_inherited" to tell compiler that you don't want to call the inherited method.
Compiler would then easily report error if it didn't find either "inherited" or "skip_inherited". That would mean you forgot. But unfortunately nobody in CodeGear thought of that.
No. That's the whole point of overriding.