Recent

Author Topic: Using inherited from direct TObject descendant.  (Read 9000 times)

damieiro

  • Full Member
  • ***
  • Posts: 200
Using inherited from direct TObject descendant.
« on: November 20, 2018, 09:25:03 am »
Talking about other issue in other topic some user pointed to use inherited in constructor in destructor.

Code: Pascal  [Select][+][-]
  1. type
  2.   MyClass= class
  3.     public
  4.       mydata:TmyData;
  5.       constructor create;virtual;
  6.       destructor destroy; override;
  7.   end;
  8.  
  9. constructor MyClass.Create;
  10. begin
  11.   inherited;
  12.   {do constructor things}
  13. end;
  14.  
  15. destructor MyClass.destroy;
  16. begin
  17.   {do destructor things}
  18.   inherited;
  19. end;
  20.  

But has it sense in a direct TObject descendant?
AFAIK tobject create and destroy methods are empty/null, and the memory reserve/destruction are performed by the constructor and destructor keyword.

Should be used inherited in a direct Tobject descendant class (other than to create a good habit)?

Has any meaning?  (perhaps using helpers changing the TObject constructor behaviour? is this possible?)

Does the compiler optimize it well if used (not doing a call for a empty create, or calling allways even to an empty code)?
« Last Edit: November 20, 2018, 09:29:53 am by damieiro »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Using inherited from direct TObject descendant.
« Reply #1 on: November 20, 2018, 11:13:50 am »
The fact that it works NOW does not matter. It is an implementation detail of the inheritance tree.
Once you start relying on this behavior (which happens all too often) and there is a need to have the TObject let anything do in constructor or destructor, you are screwed.
While such a change  would be  perfectly legal (if unlikely because it breaks things like you did).
Furthermore it is probably the ONLY place where you can leave it out. It is better to teach yourself proper programming than teach yourself shortcuts that may or may not work in the future.
Hence I suggested to always call inherited: better safe than sorry. Calling inherited on the Destructor should be done most of the time anyway since that is always virtual.
Anything that uses code that allocates/deallocates memory and / or need initialization/finalization in an ancestor will cause problems otherwise,

There's also a chain of responsabiliy to consider: technically (but not recommended) it is possible to skip the call to inherited, but then you need to clean up such code in your own destructor..
« Last Edit: November 20, 2018, 11:22:45 am by Thaddy »
Specialize a type, not a var.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Using inherited from direct TObject descendant.
« Reply #2 on: November 20, 2018, 12:10:16 pm »
Well, i have other point of view.

I agree with inherited as you say, but i don't think it should be the way for direct descendants of TObject. I think these should be viewed as Root Classes.

Free pascal doesn't allow a Root Class like C++. All classes descend from Tobject.

It could be argueably that the fact that a Class (without name) descend NOW from TObject could be not be the Default in future and is also a implementation issue.

More arguments. One could think that TObject is simply too big class (with a lot of info) that could not be needed for more lightweighted view. Or other could think it's actually too light and should be needed more heavier punch (like have more debug infor)

Or even, that The Memory Creation of a object could be in the declarative part, not as a call by a specific key constructor method.  (constructor should be used for extra memory needed and init, not the getmen (class))

In this case, i'm not telling about shorcuts. Tobject.create is defined as an Empty method. And a Root Class Method (as an habit) should never call inherited. Because if tomorrow there is no TObject as ancestor, no inherited from the code should be removed. (or the .create method is simply renamed..)

I see Tobject as a tricky and helpful class. Useful for compiler and giving a starting place like system unit, but i do not see Tobject as a pure ancestor. I see it as a way of getting compiler info (like class.name and things like that) that is implemented in shape of Tobject for many good practical reasons and incorporated as a class. For example, Tobject class give the name of the class (and a specialized class in brackets) but it's never doing that initialization in the create method. And even if you don't call inherit, the Tobject class do a lot of things....

But i can't think in TObject as an ancestor with any line of code to call or inherit from it or it will be a bias for their descendants. For that reason, i think that a class derived only from TObject, conceptually should not call inherited in any method and should be used as a Root class.

Even i dislike that constructor create or destructor destroy cannot be hidden effectively (issue: any class in free pascal can be created with Class.create even if it hasn't any create constructor and no compiler error, so you get a valid pointed class with their init values being a mess).

I often, would like that the current constructor (create) of TObject wouldn't be declared....

But well, it's my point of view. I think that in the chain of responsability, Tobject should not share any responsability. And it would be erroneus (in my way of thinking) it would have any in a future.


And well, i think also that the point of view you show has very good strenghts:

- Gives a Template that can be used for all classes, even the Tobject descendants.
- Normalise code.
- Enforces good habits.

But i see:
- Possible compiler issues (that these inherits should be removed and be optimized) -> i do not know how fpc solves it.
- Give TObject a responsability it shouldn't have (and never have)
- If in a future a Class doesn't descent from TObject, a inherited call that should be interpreted as no-call (to avoid unknow behaviour)
- If you have a very different constructor with needed parameters, it should have even less sense (really it starts its chain of construction), so previous mess should not be inherited.

« Last Edit: November 20, 2018, 02:57:29 pm by damieiro »

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Using inherited from direct TObject descendant.
« Reply #3 on: November 20, 2018, 04:31:46 pm »
...inherited from the code should be removed. (or the .create method is simply renamed..)...
Inherited without a method name behaves the way you want. The compiler searches for inherited methods with the same parameters. If found - inserts a call to this method, if not - does nothing.
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2.  
  3. type
  4.   TMyClass = class
  5.   public
  6.     procedure NotExistsInParent;
  7.   end;
  8.  
  9. procedure TMyClass.NotExistsInParent;
  10. begin
  11.   inherited;
  12.   { DoNothing }
  13. end;
  14.  
  15. begin
  16. end.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Using inherited from direct TObject descendant.
« Reply #4 on: November 20, 2018, 05:11:46 pm »
Correct. But only if there is no inherited to call. I hope you agree it is a better coding style to call inherited.
Specialize a type, not a var.

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Using inherited from direct TObject descendant.
« Reply #5 on: November 20, 2018, 06:44:38 pm »
...I hope you agree it is a better coding style to call inherited.
Certainly. Of course, I do not encourage writing inherited in each method. But I also think it should be in the constructor and destructor.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Using inherited from direct TObject descendant.
« Reply #6 on: November 20, 2018, 08:43:29 pm »
But...

@Aserge
That inherited would call allways to an empty TObject.Create, there is an ancestor class.  If the inherited is TObject is that inherited supressed or compiler makes a call and a return for nothing? (unless optimized again)..

@Thaddy
I agree with the code style, but i do not see TObject as a pure ancestor (do init things that aren't called by create constructor and it has not any class constructor). This is the way we difer. The style you propose it's solid even without ancestor method. So i really agree with that even more with the Aserge explanation. For example.


Code: Pascal  [Select][+][-]
  1. type
  2.   MyClass= class
  3.     public
  4.       mydata:TmyData;
  5.       constructor create (data:word);virtual;
  6.       destructor destroy; override;
  7.   end;
  8.  
  9. constructor MyClass.Create (data:word);
  10. begin
  11.   inherited;  {there is not such ancestor and this call is removed from compiler}
  12.   {do constructor things}
  13. end;
  14.  
  15. destructor MyClass.destroy;
  16. begin
  17.   {do destructor things}
  18.   inherited;
  19. end;
  20.  

But with TObject.create (without parameters) this call should be allways treated as in this example (inherited removed by compiler), not making me a chain of unnecesary inherited to an object that never should have code being called. I'm not sure of this case and i do not like in the .create without parameters for that reason..
Really i do not know why a .create constructor is defined as default in Tobject... It should work better without that definition
« Last Edit: November 20, 2018, 09:12:02 pm by damieiro »

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Using inherited from direct TObject descendant.
« Reply #7 on: November 22, 2018, 02:17:45 pm »
@Thaddy, Aserge:

Perhaps i'm doing a stupid question... but...
why TObject.create is virtual, but not abstract?

If TObject.create method is empty code and needed to be declared, it should be abstract, and same line or reasoning could be for .destroy . If not, i think it won't be enforcing good coding habits (not declaring constructors and destructors...) . I even think that it shouldn't even exists..

I think create/destroy aren't abstract to allow to write very simple class without declaring constructors and destructors like this and the default being empty code...

Code: Pascal  [Select][+][-]
  1. type
  2.   SimpleClass= class
  3.     public
  4.       mydata:TmyData;
  5.       procedure doThingswithMyData;
  6.   end;
  7.  

But allowing a class without enforcing constructor and destructor seems going against good coding practices... (but gives less verbose code, but i like verbose ...  :D) Well, i think that the coding styles are let to the coder, but not enforced in a way or other by the compiler. If one doesn't want to create a constructor or destructor it has other things like advanced records...



« Last Edit: November 22, 2018, 02:35:02 pm by damieiro »

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Using inherited from direct TObject descendant.
« Reply #8 on: November 23, 2018, 01:00:16 am »
why TObject.create is virtual, but not abstract?
Constructor is not virtual. But can (it blows C++ mind).
The lack of need for each class to redeclare the constructor seems to me a language advantage. This is why the constructor is not abstract.
Quote
It could be argueably that the fact that a Class (without name) descend NOW from TObject could be not be the Default in future and is also a implementation issue.
I always write =class(TObject) to avoid ambiguity.

Personally, I'm used to do "inherited Create" after Delphi.Net where it was mandatory.

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: Using inherited from direct TObject descendant.
« Reply #9 on: November 23, 2018, 04:08:12 pm »
Isn't the CONSTRUCTOR basically compiler magic to start with?

Its like the NEW in object land, the compiler uses compile time info to allocate memory for the instance and then
calls the Constructor method supplied by coder before returning to set the instance variable?


The only true wisdom is knowing you know nothing

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: Using inherited from direct TObject descendant.
« Reply #10 on: November 23, 2018, 08:16:10 pm »
Maybe it will be better understandable, if a little to reveal the internal structure.
Let there is such code
Code: Pascal  [Select][+][-]
  1. constructor TSomeClass.Create(Param1: Integer);
  2. begin
  3.   // Body
  4. end;
The compiler generates a record that contains information about the size of a base class, a virtual method table, a table of supported interfaces (optional), class name (if involved RTTI), the names of methods and properties (if involved RTTI), etc. Let's call this record VMT. As well as this function:
Code: Pascal  [Select][+][-]
  1. function TSomeClassCreate(Self: Pointer; PVmt: Pointer; Param1: Integer): TSomeClass;
  2. begin
  3.   if Self = Pointer(1) then
  4.     Self := TClass(PVmt).NewInstance;
  5.   if Self <> nil then
  6.   try
  7.     Self.Body;
  8.     if PVmt <> nil then
  9.       Self.AfterConstruction;
  10.   except
  11.     Self.Destroy;
  12.     raise;
  13.   end;
  14.   Result := Self;
  15. end;
As a result, when called
Code: Pascal  [Select][+][-]
  1. Instance := TSomeClase.Create(2); // It will
  2. Instance := TSomeClassCreate(Pointer(1), @VmtOfTSomeClass, 2); // Call NewInstance, Body(2), AfterConstruction, use Result
Code: Pascal  [Select][+][-]
  1. Intance.Create(3); // It will
  2. TSomeClassCreate(Instance, Pointer(-1), 3);  // Call Body(3), AfterConstruction, ignore Result
Code: Pascal  [Select][+][-]
  1. inherited Create(4); // It will
  2. TSomeClassCreate(Self, nil, 4);  // Call Body(4), ignore Result
At the expense of "Self := TClass(PVmt).NewInstance;" it is not necessary to override the constructor for the new class, because the compiler will substitute the correct vmt in the place of the call. Moreover, vmt can be defined not statically, but dynamically, i.e. virtual constructors appear.

garlar27

  • Hero Member
  • *****
  • Posts: 652
Re: Using inherited from direct TObject descendant.
« Reply #11 on: November 23, 2018, 10:02:37 pm »
Maybe it will be better understandable, if a little to reveal the internal structure.
Let there is such code
Code: Pascal  [Select][+][-]
  1. constructor TSomeClass.Create(Param1: Integer);
  2. begin
  3.   // Body
  4. end;
The compiler generates a record that contains information about the size of a base class, a virtual method table, a table of supported interfaces (optional), class name (if involved RTTI), the names of methods and properties (if involved RTTI), etc. Let's call this record VMT. As well as this function:
Code: Pascal  [Select][+][-]
  1. function TSomeClassCreate(Self: Pointer; PVmt: Pointer; Param1: Integer): TSomeClass;
  2. begin
  3.   if Self = Pointer(1) then
  4.     Self := TClass(PVmt).NewInstance;
  5.   if Self <> nil then
  6.   try
  7.     Self.Body;
  8.     if PVmt <> nil then
  9.       Self.AfterConstruction;
  10.   except
  11.     Self.Destroy;
  12.     raise;
  13.   end;
  14.   Result := Self;
  15. end;
Is that really necessary?
IMHO I don't think so. If you need to know the internals of object creation and allocation I'm sure you can find it somewhere in the FPC code.

Further more if you wish you can learn how it works its strength and weaknesses (if there is something you do not understand well you can ask to the core developers FWIK they don't loose opportunity to teach the internal to people who's eager to learn and try to improve things up). Then if you can see a way to improve the class creation you can do an experiment to confirm if what you did was a real improvement. Then and only then (IMHO) you can go back to them with your improvement to be reviewed and tested.
I have lost the count of how many times appears someone who have ideas to be implemented but do not want to participate in the design, development and testing. Is more like: "Let's go that way and do this and that. Quickly! And tell me when everything is done and working".

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: Using inherited from direct TObject descendant.
« Reply #12 on: November 23, 2018, 10:43:38 pm »
I found it very interesting, thanks ASerge
The only true wisdom is knowing you know nothing

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Using inherited from direct TObject descendant.
« Reply #13 on: November 24, 2018, 02:38:49 pm »
Is that really necessary?
IMHO I don't think so. If you need to know the internals of object creation and allocation I'm sure you can find it somewhere in the FPC code.

Further more if you wish you can learn how it works its strength and weaknesses (if there is something you do not understand well you can ask to the core developers FWIK they don't loose opportunity to teach the internal to people who's eager to learn and try to improve things up). Then if you can see a way to improve the class creation you can do an experiment to confirm if what you did was a real improvement. Then and only then (IMHO) you can go back to them with your improvement to be reviewed and tested.
I have lost the count of how many times appears someone who have ideas to be implemented but do not want to participate in the design, development and testing. Is more like: "Let's go that way and do this and that. Quickly! And tell me when everything is done and working".
What are you talking about? ASerge merely showed how the instantiation of classes is currently handled by the compiler/RTL behind the scenes. Nowhere I saw him suggesting to improve anything, it's simply the status quo.

Isn't the CONSTRUCTOR basically compiler magic to start with?

Its like the NEW in object land, the compiler uses compile time info to allocate memory for the instance and then
calls the Constructor method supplied by coder before returning to set the instance variable?

Not entirely. While the real calling convention of a constructor is indeed compiler magic the compiler knows about TObject's constructor (or more precisely the fact that TObject has a constructor) and uses that knowledge. In C++ on the other hand a class does not need to have a constructor thus the compiler needs to generate a default one. That's not the case in Object Pascal.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Using inherited from direct TObject descendant.
« Reply #14 on: November 25, 2018, 10:56:38 am »
Quote
Not entirely. While the real calling convention of a constructor is indeed compiler magic the compiler knows about TObject's constructor (or more precisely the fact that TObject has a constructor) and uses that knowledge. In C++ on the other hand a class does not need to have a constructor thus the compiler needs to generate a default one. That's not the case in Object Pascal
.

Well, after reading all the post, i think that the issue is this: Free pascal chooses (by design) have a common root class, with (INMHO) has good pros, but several cons.

Good pros:
- Common destroy method (you can do freeandnil for every class)
- Common create method.
- No need for constructor and destructor declaration.
- Nice syntax for "compiler magic" like classname as being incorporated in tobject.

Cons:
- No way to override .create method effectively. You can have any class with a different constructor parameters (for example .create (neededparam1:integer) ) and a call to .create without parameters allways give a valid memory reference and this can be an error producer. -> Can .create be overrided easily for these cases? (i would love that a create (param1) could override a create without params... or things like that.
- No way to make a lighter class not dependant of TObject or without predefined methods, constructors or destructor.
- Not all are in .create method of Tobject. Some things like "class name" are initialiced by "compiler magic" (i like this term to call the compiler-black-box :D)

Doubts:
- Inherited create could be a waste of time calling it to a TObject unless optimized? (is really optimiced to produce no code?). I only see that if inherited is used and no ancestor, it's removed (good, optimal), but in .create there is an ancestor.

And, well, my wishes....
I think (inmho) what is blowing me (:D ) is that all classes have their default constructor and destructor Even if already have one. I feel more confortably if a class has his default constructor if there isn't already a constructor. Or better, a default constructor.
And i know the way free pascal is doing it. Perhaps it could be extended by syntax. A "default" keyword could do the job (or some other keyword). For example, a class tree can only have one "default" constructor (declared as "default constructor") and if other "default constructor" is declared, then takes the job of the old. So you can declare all constructor you would like and only one "default" that it's checked.
Example of some enhanced syntax that could behave like before and after:

Code: Pascal  [Select][+][-]
  1. type TObject=class  
  2.   public    
  3.      default constructor create;  {a default constructor if not one declared, an example of enhanced syntax -> not current in fpc}
  4.      default destructor destroy;
  5.   end;
  6.  

Code: Pascal  [Select][+][-]
  1. type myclass=class  (TObject);
  2.   public    
  3.      default constructor create (param1:integer);  {it hides the .create constructor, because other is declared}
  4.      constructor create (param1, param2:integer);
  5.   end;
  6.  

Other options: that override (and/or reintroduce) could be used to override efectively other chosen method not necesary the same with same parameters.
This options has far more power because it could be used for all methods.
Example
Code: Pascal  [Select][+][-]
  1. type myclass=class  (TObject);
  2.   public    
  3.      constructor create (param1:integer); Override create; {extending override sintax to override exactly a chosen method; same would be for reintroduce}
  4.                       {overriding one method with new one even if it's not having same or matching ones parameters. A tool to deprecate methods}
  5.      constructor create (param1, param2:integer);
  6.   end;
  7.  


Note: My FPC knowledge is not too far. Perhaps the above examples have their own way of FPC of doing these, but i do not find these.

Note 2: Is there a forum to propose extended syntax to Devs or see the current syntax developing?. -> I'm really a newbie in the forum :)
« Last Edit: November 25, 2018, 11:45:57 am by damieiro »

 

TinyPortal © 2005-2018