I have seen a piece of code like this (not mine).
The author sets FField on the first line in the constructor before calling inherited. I am curious if this code valid.
TTest = class(TBaseObject, ITest)
private
protected
FField: TObject;
end;
constructor TTest.Create(aField: TObject);
begin
FField:= aField;
inherited Create();
...
end;
I think the memory for FField was already allocated and nilled at that point. Right?
The code is valid. By the time your constructor runs, the instance has been allocated and default initialised.
Setting a member before calling the inherited constructor means that if the inherited constructor accesses that member, it will read the value provided by the call to the derived constructor. Whilst the inherited constructor cannot directly access the member, in this case, it could access it by calling a virtual method. Note, however, that calling virtual methods from constructors is a somewhat dubious practice, because the instance may be partially constructed.
Related
I try to inherit from Tdictionary and somehow the default comparer is lost. This is what I do in essence:
type
TinpVar = class
end;
TinputVars = class(Tdictionary<string,TinpVar>)
end;
TLVRvars = class(TinputVars)
constructor create;
end;
constructor TLVRvars.create;
begin
inherited;
end;
var LVRvars : TLVRvars;
begin
LVRvars:=TLVRvars.create;
With this construction I get an AV when adding a key/value pair to LVRvars. Eventually I found that this can be prevented by changing the constructor of the inherited class to
constructor TLVRvars.create;
begin
inherited create;
end;
I do not understand why I have to do that. Although my problem is solved, I would still like to know.
In your constructor
inherited;
calls the constructor with identical parameter list to your constructor. Your constructor has no parameters, and so inherited calls the do nothing constructor in TObject. Not only have you lost your comparer, but your instance is missing the rest of the necessary steps in construction.
When you replace it with
inherited Create;
the compiler instead performs normal method resolution. It looks up the class ancestor list and calls the first method which it can. In that case this is:
constructor Create(ACapacity: Integer = 0); overload;
Hence your instance is properly created.
The documentation is here: http://docwiki.embarcadero.com/RADStudio/en/Methods#Inherited
Key excerpts are:
If inherited is followed by the name of a member, it represents a normal method call
and
When inherited has no identifier after it, it refers to the inherited
method with the same name as the enclosing method or, if the enclosing
method is a message handler, to the inherited message handler for the
same message. In this case, inherited takes no explicit parameters,
but passes to the inherited method the same parameters with which the
enclosing method was called. For example:
inherited;
occurs frequently in the implementation of constructors. It calls the
inherited constructor with the same parameters that were passed to the
descendant.
It's pretty weird isn't it. On the face of it, it seems astonishing that different methods are called. The key point though is that plain inherited leads to exact matching of parameter lists. And your method has no parameters.
On the other hand inherited Create is a standard method call. In that latter case, you end up calling a method with one parameter, using the default value for that parameter. So whilst it looks like you are calling a parameterless constructor you are not. You are passing one parameter, ACapacity, and a value of 0.
The comparator is a object that need to be created itself. If you had no constructor in your descended class I would expect the default constructor to be created because you would be implicitly calling the inherited constructor. If you create your own constructor you should always called the inherited Create (in my opinion) to allow the ancestor to do its work - in this case creating default comparator.
The one with virtual/dynamic
// declare in Child Class
constructor Create; virtual;
constructor TChildClass.Create;
begin
inherited;
end;
The one with override.
// declare in Child Class
constructor Create; override;
constructor TChildClass.Create;
begin
inherited;
end;
The one with nothing
// declare in Child Class
constructor Create;
constructor TChildClass.Create;
begin
inherited;
end;
Are these the same thing? It looks confusing.
Yes, there is a difference, but let's deal with the virtual keyword in more basic OOP terms first, yet still how it applies to Delphi methods.
When you declare a derived (child) class, and implement a method as "override", it means that you're overriding (surprise) the matching method of the base class.
This means that you can write code like this:
var child : TBaseClass;
begin
child := TChildClass.Create; // note that it is stored in TBaseClass variable
child.VirtualMethodDefinedInBaseClassThatHasBeenOverriddenInChildClass;
This will call the method in the child class, even if the variable is defined to be of the base class type. This is the whole purpose of virtual methods, you can access the object through a reference of a more general type, and still call methods that have been written for the particular type of object you're dealing with.
If you have a virtual method in the base class that you chose not to override in the child class, but instead reintroduce, you're effectively replacing it in some cases. Note that in most cases you need to tell the compiler that you really meant to do this, though I'm unsure about what Delphi requires here.
Basically, if your variable is of type TBaseClass, and you call a virtual method on it, that has been reintroduced in the TChildClass, it will still call the method in the base class.
However, if your variable is of type TChildClass, and you call that method on it, you will get the new method instead.
Now, for constructors, in Delphi, it is slightly different.
The point of virtual constructors is to be able to virtually construct objects, and to do that, Delphi also has "class types".
You can say this:
type TClassToUse = class of TBaseClass;
var cls : TClassToUse;
obj : TBaseClass;
begin
cls := TChildClass;
obj := cls.Create;
(note that my Delphi knowledge is a bit rusty here, if anyone spots bugs or glaring problems in the above code, please let me know or just fix it)
Here we store a "class" in a variable, and then ask the class to please construct an object for us. This allows us to switch out which class to create, but we also need to declare the constructors we want to use virtual, otherwise we will have problems.
So in the above code, if you declared the constructor as virtual in TBaseClass, and then override it in TChildClass (which the code is actually using in cls), the overridden constructor is the one that will be used.
If, on the other hand, you don't declare the constructor as virtual, we're back to the base class constructor. Virtual basically means to figure out the right method to execute at runtime, whereas non-virtual will figure it out at compile time.
Reintroduction as described for normal methods above, also works this way.
However, virtual constructors are only used as virtual when used through a class type.
No, static and virtual methods are not the same thing.
And override is a case of virtual method.
http://en.wikipedia.org/wiki/Virtual_function
http://docwiki.embarcadero.com/RADStudio/XE4/en/Methods#Method_Binding
Constructors bring nothing special here - they conform to the same rules as other methods for the question
I'm writing a class with some virtual/abstract procedures which I expect to get overridden. However they might not be overridden as well, which is also fine. The problem is finding out whether one of these procedures was actually overridden or not. If they're not overridden, I shouldn't try to call those procedures, and need to go about something different instead. If I try to call one of them and isn't overridden, I get Abstract Error.
Is there a way I can detect whether these procedures were overridden or not?
Here's a sample of how they're declared:
type
TMyClass = class(TObject)
protected
procedure VProc; virtual;
procedure VAProc; virtual; abstract;
end;
You can do something like this. Note that I've removed "abstract". It may work with "abstract" in place, but I haven't tried that.
type
TMyClass = class(TObject)
protected
procedure VProc; virtual;
procedure VAProc; virtual; //abstract;
end;
function GetVAProcAddress(Instance: TMyClass): pointer;
var
p: procedure of object;
begin
p := Instance.VAProc;
result := TMethod(p).Code;
end;
//in one of the TMyClass methods you can now write:
if GetVAProcAddress(self) <> #TMyClass.VAProc then
I think this is the wrong approach, it smells really really bad.. Some suggestions:
Use non-abstract methods that do what you want to do when they are not overridden.
Use events instead of methods. It's easy to check whether they have been assigned.
A method that may or may not be overridden is not an abstract method, it is merely virtual.
An abstract method is one that has no base implementation and for which an implementation must be provided by a descendant.
In your case, simply declare them as virtual and provide them with the NO-OP (No operation) default implementation that your design dictates:
type
TMyBaseClass = class
protected
procedure SomeProc; virtual;
end;
procedure TMyBaseClass.SomeProc;
begin
// NO-OP
end;
Note - this illustrates my own personal convention of documenting a deliberate NO-OP, rather than just leaving an empty implementation.
Any shennanigans you go through to attempt to detect whether a declared abstract method has been overridden or not and to call it - or not - on the basis of that test is likely to cost more time than simply calling a NO-OP implementation. Plus, should you ever need to introduce an implementation in that base class you don't have to change the method from abstract to non-abstract (possibly disrupting those "detection" circuits you had w.r.t that method, and certainly rendering their cost as nothing but pure overhead).
Use RTTI to get the method address of the virtual method in question for the class in questions. Compare it to the method address for the same method of the base class. If it is not the same, then the method was overriden.
If you got a line code like this:
lObj := TMyClass.Create;
you will notice that the compiler will output a warning saying that you are constructing instance of 'TMyClass' containing abstract method TMyClass.VAProc.
In searching for a highly intermittent memory corruption in a Delphi XE program, I have found a class constructor which initializes a few fields in the class, and then calls inherited. I believe the initializations were added after the constructor was first written, and accidentally in the wrong place. I have now corrected it to call inherited first. The exceptions from the memory corruption almost always occur in a method of this class.
Question: is it possible that this mistake caused an intermittent memory corruption? In tracing the code, it seems not, but I would really like this fix to be what solves the intermittent issue. That it does not occur for a while after fixing the problem will not prove that it has gone away.
Some code:
Tmyclass = class
ctype : integer;
ts : tstringlist;
th : thandle;
public
Constructor Create;
Destructor Destroy; override;
...
end;
Constructor Tmyclass.Create;
begin
ctype := 3;
doinit;
inherited;
end;
Here are typical steps of an object creation:
Memory allocation of the object instance;
Fill all memory with zero (to initialize all fields, especially string);
Call all nested constructors, beginning with the latest child, letting inherited call every parent - that is why you shall write inherited in both constructors and destructors.
So, inherited calls the parent method - you can even specify a parent level, or call none if you are sure you can do that (but may break the SOLID principles).
In fact, when a constructor is called, there is an hidden parameter added to the method:
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.
A value of False in the flag parameter of a destructor call indicates
that the destructor was invoked using the inherited keyword. In this
case, the destructor behaves like an ordinary method. A value of True
in the flag parameter of a destructor call indicates that the
destructor was invoked through an instance object. In this case, the
destructor deallocates the instance given by Self just before
returning.
The flag parameter behaves as if it were declared before all other
parameters. Under the register convention, it is passed in the DL
register. Under the pascal convention, it is pushed before all other
parameters. Under the cdecl, stdcall, and safecall conventions, it is
pushed just before the Self parameter.
Source: official Delphi documentation
So you can be sure that, wherever the inherited is called, it will be safely handled. For instance, initialization of fields (reset to 0) will be processed only once, before all constructors are called.
The TObject.Create default constructor (the one called in your inherited line) is just a begin end void block, which does nothing. It is not even necessary/mandatory to call inherited here, but it is a good habit, since if you change your object hierarchy, it may be needed afterall.
Only issue may be if some fields are set inside this inherited method (ctype := 2 e.g.), after having been set in the child - but this is not compiler's fault, this is up to user code!
Initializing some fields before calling the inherited constructor is not necessarily a bug. Sometimes the inherited constructor calls some virtual methods which have been overridden by the descendant and these new implementations rely on those fields to be correctly initialized.
(I am not saying that this is good design, but it's not a bug.)
In Delphi you can initialize object fields before calling inherited constructor (it does not worked in Turbo Pascal or 'old' object model, but it is allowed in Delphi 'new' object model).
Rant: Should I call "inherited" in the constructor of a class derived from TObject or TPersistent?
constructor TMyObject.Create;
begin
inherited Create; // Delphi doc: Do not create instances of TPersistent. Use TPersistent as a base class when declaring objects that are not components, but that need to be saved to a stream or have their properties assigned to other objects.
VectorNames := TStringList.Create;
Clear;
end;
Yes. It does nothing, true, but it's harmless. I think there is value in being consistent about always calling the inherited constructor, without checking to see if there is, in fact, an implementation. Some will say that it's worth calling inherited Create because Embarcadero might add an implementation for TObject.Create in the future, but I doubt this is true; it would break existing code which does not call inherited Create. Still, I think it is a good idea to call it for the reason of consistency alone.
I always do this.
If you are refactoring and move code to a common ancestor, calling the inherited Create has the following advantages:
If the common ancestor has a constructor, you can't forget to call it.
If the common ancestor has a constructor with different parameters, the compiler warns you for this.
You can also override "procedure AfterConstruction". This procedure is always called, no matter what kind of constructor.
public
procedure AfterConstruction; override;
end;
procedure TfrmListBase.AfterConstruction;
begin
inherited;
//your stuff, always initialized, no matter what kind of constructor!
end;
For example: if you want to create an object with a different constructor than the normal TObject.Create, such as TComponent.Create(AOwner) or a custom (overloaded) constructor, you can get problems because your override is not called and (in this case) your "VectorNames" variable will be nil.
I call it, except when i need a very optimized constructor.