Recent

Author Topic: Virtual constructor -- Is this necessary?  (Read 1681 times)

egsuh

  • Hero Member
  • *****
  • Posts: 1266
Virtual constructor -- Is this necessary?
« on: October 24, 2020, 04:41:53 am »
At first, I thought this is not necessary.
But when I use class reference, I need virtual method + override. Not sure this is a bug or not.

The gist of my example is as follows.


Code: Pascal  [Select][+][-]
  1. type
  2.     TParent = class
  3.  
  4.         constructor Create;
  5.     end;
  6.  
  7.     TChild1 = class(TParent)
  8.          constructor Create;
  9.     end;
  10.  
  11.     TChild2 = class(TParent)
  12.           constructor Create;
  13.      end;  
  14.  
  15.     TClassRef = class of TParent;
  16.  
  17.  
  18.     function ClassRef(hier: integer) : TClassRef;
  19.  
  20. var
  21.     AMember : TParent;
  22.  
  23. implementation
  24.  
  25. function ClassRef(hier: integer): TClassRef;
  26. begin
  27.       case hier of
  28.         0 : Result := TParent;
  29.         1 : Result := TChild1;
  30.         2 : Result := TChild2;
  31.      end;
  32. end;
  33.  
  34. begin
  35.       AMember := ClassRef(2).Create;
  36. end;
  37.  

In this case, TChild2.Create is not called, which I expected. Is this intended or a kind ob bug?
« Last Edit: October 24, 2020, 07:16:42 am by egsuh »

egsuh

  • Hero Member
  • *****
  • Posts: 1266
Re: Virtual constructor -- Is this necessary?
« Reply #1 on: October 24, 2020, 07:20:20 am »
When I add virtual/override, then it works correctly.

jamie

  • Hero Member
  • *****
  • Posts: 6077
Re: Virtual constructor -- Is this necessary?
« Reply #2 on: October 24, 2020, 12:30:45 pm »
That is correct, if you plan on having the chain of command executed then they need to be linked together and that is only done via a virtual method where the addresses get put in a table..

 So calling the first Constructor will actually call the newest one at the top, that is, the one you just inherited to.

 which is why its also important to first call the "inherited" which calls the next one down and that one should do the same.

 At some point it reaches the bottom class, the first one where it was originally created and thus does its code then returns, and it returns back to the next higher one and repeats this until it reaches your top one, the newest..

 This is also a good reason if you were to type cast over the class to attempt access to a lower level constructor or method of virtual, the execution of it would simply call the newest one first so there is no gain in casting in this manner.

 Of course there are ways to get around that limitation  ;)
The only true wisdom is knowing you know nothing

ASerge

  • Hero Member
  • *****
  • Posts: 2212
Re: Virtual constructor -- Is this necessary?
« Reply #3 on: October 24, 2020, 09:52:34 pm »
In this case, TChild2.Create is not called, which I expected. Is this intended or a kind ob bug?
Are you sure?
This:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. type
  5.   TParent = class
  6.   end;
  7.  
  8.   TChild1 = class(TParent)
  9.   end;
  10.  
  11.   TChild2 = class(TParent)
  12.   end;
  13.  
  14.   TClassRef = class of TParent;
  15.  
  16. function ClassRef(Hier: Integer): TClassRef;
  17. begin
  18.   case Hier of
  19.     0: Result := TParent;
  20.     1: Result := TChild1;
  21.   else
  22.     Result := TChild2;
  23.   end;
  24. end;
  25.  
  26. procedure Test;
  27. var
  28.   AMember: TParent;
  29. begin
  30.   AMember := ClassRef(2).Create;
  31.   try
  32.     Writeln(AMember.ClassName);
  33.   finally
  34.     AMember.Free;
  35.   end;
  36. end;
  37.  
  38. begin
  39.   Test;
  40.   Readln;
  41. end.
print 'TChild2'.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Virtual constructor -- Is this necessary?
« Reply #4 on: October 25, 2020, 01:26:39 am »
At first, I thought this is not necessary.
But when I use class reference, I need virtual method + override. Not sure this is a bug or not.
Since you're using class variable (TClassRef , "class of") then you should use virtual constructors.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Virtual constructor -- Is this necessary?
« Reply #5 on: October 25, 2020, 04:40:56 am »
Are you sure?
This:
[...]
print 'TChild2'.
Modified your example a bit:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. type
  5.   TParent = class
  6.   end;
  7.  
  8.   TChild1 = class(TParent)
  9.   end;
  10.  
  11.   TChild2 = class(TParent)
  12.   public constructor Create;
  13.   end;
  14.  
  15.   TClassRef = class of TParent;
  16.  
  17. constructor TChild2.Create;
  18. begin
  19.   WriteLn('Constructor called');
  20. end;
  21.  
  22. function ClassRef(Hier: Integer): TClassRef;
  23. begin
  24.   case Hier of
  25.     0: Result := TParent;
  26.     1: Result := TChild1;
  27.   else
  28.     Result := TChild2;
  29.   end;
  30. end;
  31.  
  32. procedure Test;
  33. var
  34.   AMember: TParent;
  35. begin
  36.   AMember := ClassRef(2).Create;
  37.   try
  38.     Writeln(AMember.ClassName);
  39.   finally
  40.     AMember.Free;
  41.   end;
  42. end;
  43.  
  44. begin
  45.   Test;
  46. end.
It does not print "Constructor called", because without making the constructor virtual/override the program does not know that TChild2 has it's own constructor, all it knows is the constructor of TParent. So here you have the situation that an object of TChild2 is created, while it's constructor isn't called. If the constructor is nontrivial and expected to be called (what most constructors are), the object would now be in an inconsistent state.

Also, as a fun side note, because this requires the base class constructor to be virtual, and TObject does not have a virtual base constructor you can't create an object using the "class of TObject" type:
Code: Pascal  [Select][+][-]
  1. type
  2.   TClassRef = class of TObject;
  3.  
  4. function CreateObject(objClass: TClassRef): TObject;
  5. begin
  6.   Result := TClassRef.Create;
  7. end;
This is a function that will create completely broken instances for every class that has a non trivial constructor

ASerge

  • Hero Member
  • *****
  • Posts: 2212
Re: Virtual constructor -- Is this necessary?
« Reply #6 on: October 25, 2020, 09:19:30 am »
Also, as a fun side note, because this requires the base class constructor to be virtual, and TObject does not have a virtual base constructor you can't create an object using the "class of TObject" type:
Code: Pascal  [Select][+][-]
  1. type
  2.   TClassRef = class of TObject;
  3.  
  4. function CreateObject(objClass: TClassRef): TObject;
  5. begin
  6.   Result := TClassRef.Create;
  7. end;
This is a function that will create completely broken instances for every class that has a non trivial constructor
You can create an instance in the function:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. type
  5.   TParent = class
  6.   public
  7.     constructor Create;
  8.   end;
  9.  
  10.   TChild1 = class(TParent)
  11.   end;
  12.  
  13.   TChild2 = class(TParent)
  14.   public
  15.     constructor Create;
  16.   end;
  17.  
  18. constructor TParent.Create;
  19. begin
  20.   WriteLn('TParent.Create');
  21. end;
  22.  
  23. constructor TChild2.Create;
  24. begin
  25.   inherited;
  26.   WriteLn('TChild2.Create');
  27. end;
  28.  
  29. function CreateObject(Hier: Integer): TParent;
  30. begin
  31.   case Hier of
  32.     0: Result := TParent.Create;
  33.     1: Result := TChild1.Create;
  34.   else
  35.     Result := TChild2.Create;
  36.   end;
  37. end;
  38.  
  39. procedure Test;
  40. var
  41.   AMember: TParent;
  42. begin
  43.   AMember := CreateObject(2);
  44.   try
  45.     WriteLn(AMember.ClassName);
  46.   finally
  47.     AMember.Free;
  48.   end;
  49. end;
  50.  
  51. begin
  52.   Test;
  53.   ReadLn;
  54. end.

egsuh

  • Hero Member
  • *****
  • Posts: 1266
Re: Virtual constructor -- Is this necessary?
« Reply #7 on: October 25, 2020, 12:28:57 pm »
Quote
TObject does not have a virtual base constructor

Actually this is why I first thought virtual constructors are not necessary. Whatever the name is, constructors must be called by

        ClassType.Create; 

And there are no way to call children's constructors with

        ParentType.Create;  (and actually run Child.Create; ).

But with class reference variable, constructors must be virtual to be overridden.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: Virtual constructor -- Is this necessary?
« Reply #8 on: October 25, 2020, 11:21:36 pm »
But with class reference variable, constructors must be virtual to be overridden.
This also is true for any static/class method. As this is only really useful for polymorphism you probably want every method you call using this mechanism to be virtual

 

TinyPortal © 2005-2018