Recent

Author Topic: How to use nested classes  (Read 1066 times)

nikel

  • Sr. Member
  • ****
  • Posts: 267
How to use nested classes
« on: December 03, 2025, 09:24:13 pm »
Hello, I'm trying to use nested classes on my console program. Here's my code:

Code: Pascal  [Select][+][-]
  1. type
  2.   {TMain}
  3.  
  4.   TMain = class
  5.   protected
  6.     BasePath        : string;
  7.     PathDelimiter   : Char;
  8.  
  9.     type
  10.       TNestedClass = class
  11.       public
  12.         Str: string;
  13.       end;
  14.   public
  15.     constructor Create(B: string);
  16.     destructor Destroy;
  17.    
  18.     procedure SetBasePath(B: string);
  19.     function GetBasePath(): string;
  20.  
  21.     procedure SetPathDelimiter(Pd: Char);
  22.     function GetPathDelimiter(): Char;
  23.  
  24.     procedure WritePath(); virtual;
  25.   end;
  26.  
  27. var
  28.   M: TMain;
  29. ...
  30.   M.TNestedClass.Str:='Test string'; // This line is giving me error
  31. ...

This is giving me error: Error: Only class methods, class properties and class variables can be referred with class references
How can I use nested classes correctly?

« Last Edit: December 04, 2025, 08:20:37 am by nikel »

dseligo

  • Hero Member
  • *****
  • Posts: 1653
Re: How to use nested classes
« Reply #1 on: December 03, 2025, 10:02:49 pm »
Try something like this:
Code: Pascal  [Select][+][-]
  1.     type
  2.       TNestedClass = class
  3.       public
  4.         Str: string;
  5.       end;
  6.  
  7.       {TMain}
  8.  
  9.       TMain = class
  10.       protected
  11.         BasePath        : string;
  12.         PathDelimiter   : Char;
  13.  
  14.         NestedClass: TNestedClass;
  15.  
  16.       public
  17.         constructor Create(B: string);
  18.         destructor Destroy;
  19.  
  20.         procedure SetBasePath(B: string);
  21.         function GetBasePath(): string;
  22.  
  23.         procedure SetPathDelimiter(Pd: Char);
  24.         function GetPathDelimiter(): Char;
  25.  
  26.         procedure WritePath(); virtual;
  27.       end;
  28.  
  29.     var
  30.       M: TMain;
  31.     ...
  32.       M := TMain.Create;
  33.       M.NestedClass := TNestedClass.Create;
  34.  
  35.       M.NestedClass.Str:='Test string'; // This line is giving me error
  36.      
  37.      
  38.       // at the end:
  39.       M.NestedClass.Free;
  40.       M.Free;
  41.     ...

nikel

  • Sr. Member
  • ****
  • Posts: 267
Re: How to use nested classes
« Reply #2 on: December 03, 2025, 10:12:48 pm »
dseligo thanks for helping.

LeP

  • Full Member
  • ***
  • Posts: 137
Re: [Solved] How to use nested classes
« Reply #3 on: December 03, 2025, 10:17:25 pm »
You can do also in this way:

Code: Pascal  [Select][+][-]
  1.   TMain = class
  2.   type
  3.     TNestedClass = class
  4.     public
  5.       Str: string;
  6.   end;
  7.   protected
  8.     BasePath        : string;
  9.     PathDelimiter   : Char;
  10.     NestedClass: TNestedClass;
  11.   public
  12.     constructor Create(B: string); overload;
  13.     destructor Destroy; override;
  14.  
  15.     procedure SetBasePath(B: string);
  16.     function GetBasePath(): string;
  17.  
  18.     procedure SetPathDelimiter(Pd: Char);
  19.     function GetPathDelimiter(): Char;
  20.  
  21.     procedure WritePath(); virtual;
  22.   end;
  23.  
  24. var
  25.   M: TMain;
  26.  
  27. implementation
  28.  
  29.  
  30. begin
  31.   m := TMain.Create('Hello');
  32.   M.NestedClass.Str:='Test string';
  33. end;
  34.  
  35. constructor TMain.Create(B: string);
  36. begin
  37.   inherited Create;
  38.  
  39.   NestedClass := TNestedClass.Create;
  40. end;
  41.  
  42. destructor TMain.Destroy;
  43. begin
  44.   if Assigned(NestedCLass) then
  45.     NestedClass.Free;
  46.  
  47.   inherited Destroy;
  48. end

440bx

  • Hero Member
  • *****
  • Posts: 6088
Re: [Solved] How to use nested classes
« Reply #4 on: December 03, 2025, 10:41:55 pm »
In addition to what @LeP mentioned, since the nested class is not strictly private it can be used in the domain of what is protected.  IOW, you could also declare classes of type TMain.TNestedClass.

For instance, in @LeP's example, you could follow the declaration of class M with another class variable of type TNestedClass.  Like this:
Code: Pascal  [Select][+][-]
  1. var
  2.   M       : TMain;
  3.   NestedN : TMain.TNestedClass;
  4.  
if you do it like that, remember to create the NestedN instance (since that one is not taken care of by TMain's constructor.)

HTH.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

nikel

  • Sr. Member
  • ****
  • Posts: 267
Re: [Solved] How to use nested classes
« Reply #5 on: December 04, 2025, 08:20:11 am »
440bx, I followed your advice but I'm getting error: identifier idents no member "TParameters". Here's my code so far:

Code: Pascal  [Select][+][-]
  1. program AutoDJ;
  2. // 0.01 İlk klasörün içindeki ilk şarkının yolunu yazar
  3.  
  4. {$mode objfpc}
  5. {$codepage UTF8}
  6. {$m+}
  7.  
  8. uses
  9.   {$IFDEF UNIX}
  10.   CThreads,
  11.   {$ENDIF}
  12.   Classes, SysUtils,
  13.   // NPos için
  14.   StrUtils;
  15.  
  16. type
  17.   TParameters = class
  18.   private
  19.     IsIndex     : Boolean;
  20.     IsVersion   : Boolean;
  21.     IsKey       : Boolean;
  22.     IsBPM       : Boolean;
  23.     IsArtist    : Boolean;
  24.     IsYear      : Boolean;
  25.   public
  26.     constructor Create(I: Boolean; V: Boolean; K: Boolean; B: Boolean; A: Boolean; Y: Boolean);
  27.  
  28.     procedure SetIsIndex(I: Boolean);
  29.     function GetIsIndex (): Boolean;
  30.    
  31.     procedure SetIsVersion(V: Boolean);
  32.     function GetIsVersion(): Boolean;
  33.  
  34.     procedure SetIsKey(K: Boolean);
  35.     function GetIsKey(): Boolean;
  36.  
  37.     procedure SetIsBPM(B: Boolean);
  38.     function GetIsBPM(): Boolean;
  39.    
  40.     procedure SetIsArtist(A: Boolean);
  41.     function GetIsArtist(): Boolean;
  42.  
  43.     procedure SetIsYear(Y: Boolean);
  44.     function GetIsYear(): Boolean;
  45.   end;
  46.  
  47.   {TMain}
  48.  
  49.   TMain = class
  50.   protected
  51.     BasePath        : string;
  52.     PathDelimiter   : Char;
  53.  
  54.     Parameters      : TParameters;
  55.   public
  56.     constructor Create(B: string);
  57.     destructor Destroy;
  58.    
  59.     procedure SetBasePath(B: string);
  60.     function GetBasePath(): string;
  61.  
  62.     procedure SetPathDelimiter(Pd: Char);
  63.     function GetPathDelimiter(): Char;
  64.  
  65.     procedure WritePath(); virtual;
  66.   end;
  67.  
  68. var
  69.   M: TMain;
  70.   P: TMain.TParameters; // This line is giving me error
  71.  
  72. procedure TParameters.SetIsIndex(I: Boolean);
  73. begin
  74.   IsIndex:=I;
  75. end;
  76.  
  77. function TParameters.GetIsIndex(): Boolean;
  78. begin
  79.   GetIsIndex:=IsIndex;
  80. end;
  81.  
  82. procedure TParameters.SetIsVersion(V: Boolean);
  83. begin
  84.   IsVersion:=V;
  85. end;
  86.  
  87. function TParameters.GetIsVersion(): Boolean;
  88. begin
  89.   GetIsVersion:=IsVersion;
  90. end;
  91.  
  92. procedure TParameters.SetIsKey(K: Boolean);
  93. begin
  94.   IsKey:=K;
  95. end;
  96.  
  97. function TParameters.GetIsKey(): Boolean;
  98. begin
  99.   GetIsKey:=IsKey;
  100. end;
  101.  
  102. procedure TParameters.SetIsBPM(B: Boolean);
  103. begin
  104.   IsBPM:=B;
  105. end;
  106.  
  107. function TParameters.GetIsBPM(): Boolean;
  108. begin
  109.   GetIsBPM:=IsBPM;
  110. end;
  111.  
  112. procedure TParameters.SetIsArtist(A: Boolean);
  113. begin
  114.   IsArtist:=A;
  115. end;
  116.  
  117. function TParameters.GetIsArtist(): Boolean;
  118. begin
  119.   GetIsArtist:=IsArtist;
  120. end;
  121.  
  122. procedure TParameters.SetIsYear(Y: Boolean);
  123. begin
  124.   IsYear:=Y;
  125. end;
  126.  
  127. function TParameters.GetIsYear(): Boolean;
  128. begin
  129.   GetIsYear:=IsYear;
  130. end;
  131.  
  132. constructor TMain.Create(B: string);
  133. begin
  134.   inherited Create;
  135.  
  136.   BasePath:=B;
  137.  
  138.   Parameters:=TParameters.Create(False, False, False, False, False, False);
  139. end;
  140.  
  141. destructor TMain.Destroy;
  142. begin
  143.   if (Assigned(Parameters)) then
  144.   begin
  145.     Parameters.Free;
  146.   end;
  147.  
  148.   inherited Destroy;
  149. end;
  150.  
  151. procedure TMain.SetBasePath(B: string);
  152. begin
  153.   BasePath:=B;
  154. end;
  155.  
  156. function TMain.GetBasePath(): string;
  157. begin
  158.   GetBasePath:=BasePath;
  159. end;
  160.  
  161. procedure TMain.SetPathDelimiter(Pd: Char);
  162. begin
  163.   PathDelimiter:=Pd;
  164. end;
  165.  
  166. function TMain.GetPathDelimiter(): Char;
  167. begin
  168.   GetPathDelimiter:=PathDelimiter;
  169. end;
  170.  
  171. procedure TMain.WritePath();
  172. var
  173.   Info      : TSearchRec;
  174. begin
  175.   FindFirst(BasePath, faAnyFile, Info);
  176.   WriteLn(Copy(BasePath, 0, LastDelimiter(PathDelimiter, BasePath)) + Info.Name);
  177.   FindClose(Info);
  178. end;
  179.  
  180. begin
  181.   M:=TMain.Create('/path/to/songs/*');
  182.   {$IFDEF UNIX}
  183.   M.SetPathDelimiter('/');
  184.   {$ENDIF}
  185.   {$IFDEF WINDOWS}
  186.   M.SetPathDelimiter('\');
  187.   {$ENDIF}
  188.   M.WritePath;
  189.   M.Free;
  190. end.
  191.  

Thausand

  • Sr. Member
  • ****
  • Posts: 458
Re: [Solved] How to use nested classes
« Reply #6 on: December 04, 2025, 09:42:03 am »
I'm getting error: identifier idents no member "TParameters". Here's my code so far:
That no nested class. Fast fix error:

Code: Pascal  [Select][+][-]
  1. program AutoDJ;
  2. // 0.01 İlk klasörün içindeki ilk şarkının yolunu yazar
  3.  
  4. {$mode objfpc}
  5. {$codepage UTF8}
  6. {$m+}
  7.  
  8. uses
  9.   {$IFDEF UNIX}
  10.   CThreads,
  11.   {$ENDIF}
  12.   Classes, SysUtils,
  13.   // NPos için
  14.   StrUtils;
  15.  
  16. type
  17.   TParameters = class
  18.   private
  19.     IsIndex     : Boolean;
  20.     IsVersion   : Boolean;
  21.     IsKey       : Boolean;
  22.     IsBPM       : Boolean;
  23.     IsArtist    : Boolean;
  24.     IsYear      : Boolean;
  25.   public
  26.     constructor Create(I: Boolean; V: Boolean; K: Boolean; B: Boolean; A: Boolean; Y: Boolean);
  27.  
  28.     procedure SetIsIndex(I: Boolean);
  29.     function GetIsIndex (): Boolean;
  30.    
  31.     procedure SetIsVersion(V: Boolean);
  32.     function GetIsVersion(): Boolean;
  33.  
  34.     procedure SetIsKey(K: Boolean);
  35.     function GetIsKey(): Boolean;
  36.  
  37.     procedure SetIsBPM(B: Boolean);
  38.     function GetIsBPM(): Boolean;
  39.    
  40.     procedure SetIsArtist(A: Boolean);
  41.     function GetIsArtist(): Boolean;
  42.  
  43.     procedure SetIsYear(Y: Boolean);
  44.     function GetIsYear(): Boolean;
  45.   end;
  46.  
  47.   {TMain}
  48.  
  49.   TMain = class
  50.   type
  51.     TParameters = AutoDJ.TParameters;
  52.   protected
  53.     BasePath        : string;
  54.     PathDelimiter   : Char;
  55.  
  56.     Parameters      : TParameters;
  57.   public
  58.     constructor Create(B: string);
  59.     destructor Destroy;
  60.    
  61.     procedure SetBasePath(B: string);
  62.     function GetBasePath(): string;
  63.  
  64.     procedure SetPathDelimiter(Pd: Char);
  65.     function GetPathDelimiter(): Char;
  66.  
  67.     procedure WritePath(); virtual;
  68.   end;
  69.  
  70. var
  71.   M: TMain;
  72.   P: TMain.TParameters; // This line is giving me error
  73.  
  74. procedure TParameters.SetIsIndex(I: Boolean);
  75. begin
  76.   IsIndex:=I;
  77. end;
  78.  
  79. function TParameters.GetIsIndex(): Boolean;
  80. begin
  81.   GetIsIndex:=IsIndex;
  82. end;
  83.  
  84. procedure TParameters.SetIsVersion(V: Boolean);
  85. begin
  86.   IsVersion:=V;
  87. end;
  88.  
  89. function TParameters.GetIsVersion(): Boolean;
  90. begin
  91.   GetIsVersion:=IsVersion;
  92. end;
  93.  
  94. procedure TParameters.SetIsKey(K: Boolean);
  95. begin
  96.   IsKey:=K;
  97. end;
  98.  
  99. function TParameters.GetIsKey(): Boolean;
  100. begin
  101.   GetIsKey:=IsKey;
  102. end;
  103.  
  104. procedure TParameters.SetIsBPM(B: Boolean);
  105. begin
  106.   IsBPM:=B;
  107. end;
  108.  
  109. function TParameters.GetIsBPM(): Boolean;
  110. begin
  111.   GetIsBPM:=IsBPM;
  112. end;
  113.  
  114. procedure TParameters.SetIsArtist(A: Boolean);
  115. begin
  116.   IsArtist:=A;
  117. end;
  118.  
  119. function TParameters.GetIsArtist(): Boolean;
  120. begin
  121.   GetIsArtist:=IsArtist;
  122. end;
  123.  
  124. procedure TParameters.SetIsYear(Y: Boolean);
  125. begin
  126.   IsYear:=Y;
  127. end;
  128.  
  129. function TParameters.GetIsYear(): Boolean;
  130. begin
  131.   GetIsYear:=IsYear;
  132. end;
  133.  
  134. constructor TMain.Create(B: string);
  135. begin
  136.   inherited Create;
  137.  
  138.   BasePath:=B;
  139.  
  140.   Parameters:=TParameters.Create(False, False, False, False, False, False);
  141. end;
  142.  
  143. destructor TMain.Destroy;
  144. begin
  145.   if (Assigned(Parameters)) then
  146.   begin
  147.     Parameters.Free;
  148.   end;
  149.  
  150.   inherited Destroy;
  151. end;
  152.  
  153. procedure TMain.SetBasePath(B: string);
  154. begin
  155.   BasePath:=B;
  156. end;
  157.  
  158. function TMain.GetBasePath(): string;
  159. begin
  160.   GetBasePath:=BasePath;
  161. end;
  162.  
  163. procedure TMain.SetPathDelimiter(Pd: Char);
  164. begin
  165.   PathDelimiter:=Pd;
  166. end;
  167.  
  168. function TMain.GetPathDelimiter(): Char;
  169. begin
  170.   GetPathDelimiter:=PathDelimiter;
  171. end;
  172.  
  173. procedure TMain.WritePath();
  174. var
  175.   Info      : TSearchRec;
  176. begin
  177.   FindFirst(BasePath, faAnyFile, Info);
  178.   WriteLn(Copy(BasePath, 0, LastDelimiter(PathDelimiter, BasePath)) + Info.Name);
  179.   FindClose(Info);
  180. end;
  181.  
  182. begin
  183.   M:=TMain.Create('/path/to/songs/*');
  184.   {$IFDEF UNIX}
  185.   M.SetPathDelimiter('/');
  186.   {$ENDIF}
  187.   {$IFDEF WINDOWS}
  188.   M.SetPathDelimiter('\');
  189.   {$ENDIF}
  190.   M.WritePath;
  191.   M.Free;
  192. end.
  193.  

Add code is highlight.

edit PS:
https://www.freepascal.org/docs-html/rtl/system/directoryseparator.html / https://www.freepascal.org/docs-html/rtl/sysutils/pathdelim.html
« Last Edit: December 04, 2025, 10:07:51 am by Thausand »

440bx

  • Hero Member
  • *****
  • Posts: 6088
Re: How to use nested classes
« Reply #7 on: December 04, 2025, 01:59:08 pm »
as @Thausand pointed out, what you've got there is _not_ a nested class therefore you cannot qualify TParameters with TClass since TParameters is not inside TClass.

Change the lines that read:
Code: Pascal  [Select][+][-]
  1. var
  2.   M: TMain;
  3.   P: TMain.TParameters; // This line is giving me error
  4.  

to:

Code: Pascal  [Select][+][-]
  1. var
  2.   M: TMain;
  3.   P: TParameters; // NOT qualified since TParameters is not inside TClass
  4.  
  5. constructor TParameters.Create(I: Boolean; V: Boolean; K: Boolean; B: Boolean; A: Boolean; Y: Boolean);
  6. begin
  7.   { add the necessary code here to create a TParameters instance }
  8.  
  9.   { ... }
  10. end;
  11.  
Note that the type of P is no longer qualified _and_ you need to declare TParameters constructor otherwise you'll get an unreferenced forward declaration for it.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

nikel

  • Sr. Member
  • ****
  • Posts: 267
Re: How to use nested classes
« Reply #8 on: December 06, 2025, 11:50:07 am »
Thanks for the replies. I can't access parent class' variables from child class. Here's my code so far:

Code: Pascal  [Select][+][-]
  1. program AutoDJ;
  2.  
  3. {$mode objfpc}
  4. {$codepage UTF8}
  5. {$m+}
  6.  
  7. uses
  8.   {$IFDEF UNIX}
  9.   CThreads,
  10.   {$ENDIF}
  11.   Classes, SysUtils,
  12.   StrUtils;
  13.  
  14. type
  15.   TParameters = class
  16.   private
  17.     IsIndex     : Boolean;
  18.     IsVersion   : Boolean;
  19.     IsKey       : Boolean;
  20.     IsBPM       : Boolean;
  21.     IsArtist    : Boolean;
  22.     IsYear      : Boolean;
  23.   public
  24.     constructor Create(I: Boolean; V: Boolean; K: Boolean; B: Boolean; A: Boolean; Y: Boolean);
  25.  
  26.     procedure SetIsIndex(I: Boolean);
  27.     function GetIsIndex (): Boolean;
  28.    
  29.     procedure SetIsVersion(V: Boolean);
  30.     function GetIsVersion(): Boolean;
  31.  
  32.     procedure SetIsKey(K: Boolean);
  33.     function GetIsKey(): Boolean;
  34.  
  35.     procedure SetIsBPM(B: Boolean);
  36.     function GetIsBPM(): Boolean;
  37.    
  38.     procedure SetIsArtist(A: Boolean);
  39.     function GetIsArtist(): Boolean;
  40.  
  41.     procedure SetIsYear(Y: Boolean);
  42.     function GetIsYear(): Boolean;
  43.  
  44.     procedure Apply;
  45.   end;
  46.  
  47.   {TMain}
  48.  
  49.   TMain = class
  50.   type
  51.     TParameters = AutoDJ.TParameters;
  52.   protected
  53.     DebugMode       : Boolean;
  54.     Parameters      : TParameters;
  55.   public
  56.     constructor Create(D: Boolean);
  57.     destructor Destroy;
  58.  
  59.     procedure SetDebugMode(D: Boolean);
  60.     function GetDebugMode(): Boolean;
  61.  
  62.     procedure WritePath();
  63.   end;
  64.  
  65. resourcestring
  66.   EDirectoryNotExists = 'Directory doesn''t exist: %s';
  67.  
  68. var
  69.   M: TMain;

440bx

  • Hero Member
  • *****
  • Posts: 6088
Re: How to use nested classes
« Reply #9 on: December 06, 2025, 12:16:24 pm »
There is no parent/child relationship between the classes TMain and TParameters.  Those two classes are "siblings" since they are declared at the same level.

You should remove/delete lines 50 and 51.  The declaration is redundant and completely unnecessary.

You should be able to access TParameters fields with M.Parameters.<field name> but, it may be necessary for the fields to be public (I don't remember the exact domain of "private", it may be enough to restrict access using M, not sure, they definitely should be accessible if you make them public.)

HTH.


FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

nikel

  • Sr. Member
  • ****
  • Posts: 267
Re: How to use nested classes
« Reply #10 on: December 06, 2025, 01:39:35 pm »
I'm tying to access DebugMode in the procedure TParameters.Apply.

Thaddy

  • Hero Member
  • *****
  • Posts: 18729
  • To Europe: simply sell USA bonds: dollar collapses
Re: How to use nested classes
« Reply #11 on: December 06, 2025, 01:40:26 pm »
A nested class simply works like so:
Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. type
  3.   TOuterclass = class
  4.   strict private type
  5.     TInnerClass = class
  6.     public
  7.       constructor create;
  8.       destructor destroy; override;
  9.     end;
  10.   public
  11.     IClass:TInnerClass;
  12.   public
  13.     constructor create;
  14.     destructor destroy;override;
  15.   end;
  16.  
  17.   constructor TOuterClass.TInnerClass.Create;
  18.   begin
  19.     inherited create;
  20.     writeln('Inner');
  21.   end;
  22.  
  23.   constructor TOuterClass.Create;
  24.   begin
  25.     inherited create;
  26.     writeln('Outer');
  27.     IClass := TInnerClass.create;
  28.   end;
  29.  
  30.   destructor TOuterClass.TInnerClass.destroy;
  31.   begin
  32.     writeln('Inner destroy');
  33.     inherited destroy;
  34.   end;
  35.  
  36.   destructor TOuterClass.Destroy;
  37.   begin
  38.     IClass.Free;
  39.     writeln('Outer destroy');
  40.     inherited destroy;
  41.   end;
  42. var
  43.   test:TOuterClass;  
  44. begin
  45.   Test := TOuterClass.Create;
  46.   test.free;
  47. end.

That ain't hard innit?
(Apart from a lot of noise above that does not help you, my example is the correct syntax for nested classes)
« Last Edit: December 06, 2025, 01:43:48 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

nikel

  • Sr. Member
  • ****
  • Posts: 267
Re: How to use nested classes
« Reply #12 on: December 06, 2025, 01:42:41 pm »
I modified my reply.

old_DOS_err

  • Jr. Member
  • **
  • Posts: 60
Re: How to use nested classes
« Reply #13 on: December 06, 2025, 01:58:13 pm »
Hi,

Just a general point of view. I use a lot of nested classes. As a guideline, I have several general purpose folders, the main one being working_out. If I am trying to figure out how something works, there can be several problems with trying to do that in the actual code. I like to go to my working out folder and then set up the simplest example possible to see something working, then I have a good grasp of it. If I can do this in DOS, even better, since then I can use WriteLn to easily show on screen what is happening.

I'm unclear how much you know, so sorry if a bit patronising, but from experience, including myself getting help, I'd rather aim too low rather than assume you (or anyone else reading this) know things and miss out some key info.

Code: Pascal  [Select][+][-]
  1. Program demo_for_forum;
  2. {$mode objfpc}{$H+}
  3.  
  4. Uses
  5.   Classes, SysUtils;
  6.  
  7. Type
  8.  
  9.   util_class = Class  // this could be anywhere, including a general utility in a unit, so long as referenced in Uses
  10.     // note: unless specifically creating objects or setting vars, there is no need for Create and Destroy, they are done in the background
  11.     a_str : String;  // by default vars and methods are public, but if uncertain, no harm adding
  12.  
  13.     Procedure set_str( Const s : String );
  14.   End;
  15.  
  16.   main_class = Class  // this could be a form
  17.   Private  // data - I have the sections placed into folding, so by labelling each section, I can easily see what each section is when class is folded
  18.     Type
  19.       nested_class = Class( util_class )  // now this is nested, ie only exists in the class
  20.         the_host : main_class;  // I use this linkage all the time in my nested classes - the huge advantage is no changes are required to the original class
  21.         Procedure call_show_str;  // this is going to call back up to the host
  22.       End;
  23.  
  24.     Var
  25.       nested_obj : nested_class;  // note this is now the extended name, not the original util class
  26.   Public  // methods
  27.     Constructor Create;
  28.     Destructor Destroy; Override;
  29.  
  30.     Procedure show_str;  // this will use the nested string
  31.     Procedure main_call;  // whatever front end is in place
  32.   End;
  33.  
  34. // main - when I have several classes in unit, I usually put a simple comment line to denote each class code and ensure all code goes in the right section, mainly for folding
  35.  
  36. Constructor main_class.Create;  // if this is for a form, place this code in form Create, or similar, so long as in control of create and free
  37.   Begin
  38.     nested_obj := nested_class.Create;  // because the var, nested_obj, is in the main class, just normal access
  39.     nested_obj.the_host := Self;  // this is the linkage line, now the nested has access to the host class by using the_host
  40.   End;
  41.  
  42. Destructor main_class.Destroy;
  43.   Begin
  44.     { if I have created the objected in Create then I know this is good, if I don't know for sure I either use a boolean flag (var), eg object_set_up,
  45.       or set var to Nil in Create and then test If object <> Nil Then FreeAndNil
  46.     }
  47.     FreeAndNil( nested_obj );  // if not freed it will cause a memory leak, good practice to test heap occasionally
  48.   End;
  49.  
  50. Procedure main_class.main_call;
  51.   Begin
  52.     nested_obj.set_str( 'demo of nested' );  // note that since nested is now a standard object, the dot is used (if you just put the dot you will get the list up)
  53.     show_str;
  54.   End;
  55.  
  56. Procedure main_class.show_str;
  57.   Begin
  58.     WriteLn( nested_obj.a_str );  // this is possible because a_str in public, play around with that
  59.   End;
  60.  
  61. // main - nested
  62.  
  63. Procedure main_class.nested_class.call_show_str;  // note the way this is set up, since nested_class does not exist by itself, it has be declared as part of main
  64.   Begin
  65.     the_host.show_str;  // this is the link back in action, it could be anything, a var, a label on a form (I often use that), etc. The dot list will show what is accessible.
  66.   End;
  67.  
  68. // nested - note this could be a in unit, just here for convenience
  69.  
  70. Procedure util_class.set_str( Const s : String );  // note the call here, since nested class only exists in main, it is only recognised within that
  71.   Begin
  72.     a_str := 'this is util class: string = ' + s;  // just to show it was adapted
  73.   End;
  74.  
  75. // general
  76.  
  77. Procedure demo_of_nested_class;
  78.   Var
  79.     main_obj : main_class;  // for forms obviously don't need this bit
  80.  
  81.   Begin
  82.     main_obj := main_class.Create;  // because the nested is set up in Create, it now exists - again, not nec in forms
  83.     main_obj.main_call;  // whatever the main routine is
  84.     FreeAndNil( main_obj );
  85.   End;
  86.  
  87. Begin
  88.   demo_of_nested_class;
  89.  
  90.   WriteLn; WriteLn('Finished...'); ReadLn;  // just stops the console window disappearing automatically
  91. End.
  92.  

Please feel free to ask for any clarification

Phil (aka old DOS err)

Thaddy

  • Hero Member
  • *****
  • Posts: 18729
  • To Europe: simply sell USA bonds: dollar collapses
Re: How to use nested classes
« Reply #14 on: December 06, 2025, 02:03:44 pm »
Here's a template based on your original problem.
The inner class has access to its outer class through the parent property.
Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. type
  3.   TMain = class
  4.   strict private type
  5.     TParameters = class
  6.     strict private
  7.      FParent:TMain;
  8.     public
  9.       constructor create(const aParent:TMain);
  10.       destructor destroy; override;
  11.       property parent:TMain read FParent;
  12.     end;
  13.   strict private
  14.     IClass:TParameters;
  15.   public
  16.     constructor create;
  17.     destructor destroy;override;
  18.   end;
  19.  
  20.   constructor TMain.TParameters.Create(const aParent:TMain);
  21.   begin
  22.     inherited create;
  23.     FParent := aParent;
  24.     writeln('Inner');
  25.   end;
  26.  
  27.   constructor TMain.Create;
  28.   begin
  29.     inherited create;
  30.     writeln('Outer');
  31.     IClass := TParameters.create(self);
  32.   end;
  33.  
  34.   destructor TMain.TParameters.destroy;
  35.   begin
  36.     writeln('Inner destroy');
  37.     inherited destroy;
  38.   end;
  39.  
  40.   destructor TMain.Destroy;
  41.   begin
  42.     IClass.Free;
  43.     writeln('Outer destroy');
  44.     inherited destroy;
  45.   end;
  46. var
  47.   test:TMain;  
  48. begin
  49.   Test := TMain.Create;
  50.   test.free;
  51. end.
Simply add your other fields to main and parameters.
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

 

TinyPortal © 2005-2018