Recent

Author Topic: Inherited Constructor (add a Parameter)?  (Read 586 times)

Nimral

  • Jr. Member
  • **
  • Posts: 97
Inherited Constructor (add a Parameter)?
« on: October 09, 2019, 09:26:56 am »
I recently though, it was handy to have a string list which can be populated when created, so I can, for instance, assign it to a Memo in one line. My idea was to overwrite the constructor like shown below:

Code: Pascal  [Select]
  1. Type TMyList = class(TStringList)
  2.     constructor create(S:String)
  3. end;
  4.  
  5. constructor TMyList.create(S:String)
  6.  
  7.     inherited;
  8.  
  9.     // ( ... do some string manipulations ...)
  10.  
  11.     // finally, populate the list
  12.     Self.Delimiter := ';';
  13.     Self.DelimitedText := S;
  14. end;
  15.  
  16. ( ....)
  17.  
  18. var MyList : TMyList;
  19.  
  20. Memo1.Lines.Assign(MyList.Create('one;two;three'));
  21.  
  22.  

But the compiler complains:

mainunit.pas(69,15) Error: Wrong number of parameters specified for call to "Create"

What am I missing?

Thnx!

Armin.
« Last Edit: October 09, 2019, 09:29:12 am by Nimral »
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

Thaddy

  • Hero Member
  • *****
  • Posts: 8868
Re: Inherited Constructor (add a Parameter)?
« Reply #1 on: October 09, 2019, 09:31:17 am »
Code: Pascal  [Select]
  1. constructor TMyList.create(S:String)
  2.  
  3.     // inherited; No: compiler assumes same signature and the parent class does not know the parameter for create.
  4.    inherited create; // has a different meaning and should work: the parent class has a parameterless create
  5.     // ( ... do some string manipulations ...)
  6.     Self.Delimiter := ';';
  7.     Self.DelimitedText := S;
  8. end;
Depending on what you want you may want to add virtual too...So you can override it later..
« Last Edit: October 09, 2019, 09:38:21 am by Thaddy »
Most people that want to use threading should learn to patch their jeans first: use a needle.

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: Inherited Constructor (add a Parameter)?
« Reply #2 on: October 09, 2019, 09:58:43 am »
Thanks, Taddy, now the compiler is happy.

Unfortunately the code crashes, seems I am not allowerd to do what I want:

Code: Pascal  [Select]
  1. constructor TMyList.create(S: String);
  2.  
  3. begin
  4.      Inherited create();
  5.      Self.Delimiter := ';';
  6.      Self.DelimitedText := S;
  7. end;
  8.  
  9. (...)
  10.  
  11. S := 'one;two;three';
  12. Memo2.Lines.Assign(MyList.Create(S));
  13.  

When the debugger hits the line "Self.Delimiter := ';';" the code crashes with an "External: SIGSEV" error.

Any ideas why?

Armin.
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

Thaddy

  • Hero Member
  • *****
  • Posts: 8868
Re: Inherited Constructor (add a Parameter)?
« Reply #3 on: October 09, 2019, 10:09:01 am »
Code: Pascal  [Select]
  1. Memo2.Lines.Assign(MyList.Create(S));
  2.  
That is wrong is so many ways... I do not have the time now to answer it, but I will come back if nobody else explains to you why it is so wrong...
Most people that want to use threading should learn to patch their jeans first: use a needle.

bytebites

  • Full Member
  • ***
  • Posts: 205
Re: Inherited Constructor (add a Parameter)?
« Reply #4 on: October 09, 2019, 10:12:33 am »
Code: Pascal  [Select]
  1. Memo2.Lines:=TMyList.Create(S);

Handoko

  • Hero Member
  • *****
  • Posts: 3152
  • My goal: build my own game engine using Lazarus
Re: Inherited Constructor (add a Parameter)?
« Reply #5 on: October 09, 2019, 10:16:01 am »
This works:

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, Forms, Controls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Memo1: TMemo;
  17.     procedure Button1Click(Sender: TObject);
  18.   end;
  19.  
  20.   { TMyList }
  21.  
  22.   TMyList = class(TStringList)
  23.     constructor Create(S:String);
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TMyList }
  34.  
  35. constructor TMyList.Create(S: String);
  36. begin
  37.   inherited Create;
  38.   Delimiter := ';';
  39.   DelimitedText := S;
  40. end;
  41.  
  42. { TForm1 }
  43.  
  44. procedure TForm1.Button1Click(Sender: TObject);
  45. var
  46.   MyList: TMyList;
  47.   S: string;
  48. begin
  49.   S := 'one;two;three';
  50.   MyList := TMyList.Create(S);
  51.   Memo1.Lines.AddStrings(MyList);
  52.   MyList.Free;
  53. end;
  54.  
  55. end.

I prefer not to use Assign.

Read more:
https://www.freepascal.org/docs-html/rtl/classes/tstrings.addstrings.html

Example from Embarcadero:
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Classes_TStrings_AddStrings.html
« Last Edit: October 09, 2019, 10:26:30 am by Handoko »

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: Inherited Constructor (add a Parameter)?
« Reply #6 on: October 09, 2019, 11:13:13 am »
Handoko & Thaddy,

Handokos sample showed me the way, thanks!

I don't mean to waste your time, but trying to evolve from traditional Pascal to Object Pascal some background infos would be greatly appreciated.

This is Handokos routine, which works:

Code: Pascal  [Select]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   MyList: TMyList;
  4.   S: string;
  5. begin
  6.   S := 'one;two;three';
  7.   MyList := TMyList.Create(S);   // 1
  8.   Memo1.Lines.AddStrings(MyList);   // 2
  9.   MyList.Free;   // 3
  10. end;

What I did try to avoid was the lines 1 and 3, since I never actually intend to do something with MyList.

Code: Pascal  [Select]
  1. Memo2.Lines.AddStrings(MyList.Create(S));  // 4
  2. Memo2.Lines.AddStrings(TMyList.Create(S));  // 5
  3.  

OK, seems I cannot use (4), guess that's just because of the way the creators of Object Pascal decided their language should work, but why doesn't the compiler complain?

Line (5) works, but does cause a memory leak, since MyList.Free is never called. I considered this, but from what I was used to, variables declared as var and not beeing pointers with memory allocated manually (New/Dispose/GetMem/FreeMem) are automatically freed when their scope is exited. I did somehow expect that the same should happen to var MyList - the destructor should be called automatically. Why wasn't it?

I did also expect that this would eventually/likely render the Memo's list invalid, and thus tried to use Memo2.Lines.Assign, since what I read from the docs this routine doesn't just assign memory addresses but make an attribute-by-attribute copy of the original strings. Seems not to be the case, is it?

Please show me - for learnings sake - where I was wrong, and how one could have known. I understand Handokos code, but I don't understand why it was necessary to write it that way, whether there are other ways, especially whether it was possible to write code the way I had in mind ( a one statement "fire and forget" StringList)

Thnx,

Armin.
« Last Edit: October 09, 2019, 11:16:01 am by Nimral »
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7430
Re: Inherited Constructor (add a Parameter)?
« Reply #7 on: October 09, 2019, 11:22:08 am »
OK, seems I cannot use (4), guess that's just because of the way the creators of Object Pascal decided their language should work, but why doesn't the compiler complain?

Because it is valid code. Just not what you want to do. 

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: Inherited Constructor (add a Parameter)?
« Reply #8 on: October 09, 2019, 12:29:57 pm »

Because it is valid code. Just not what you want to do. 


Seems so :-) What then is the correct use of either? In other words - what's the difference between MyList.Create and TMyList.Create? When to use which one?
Armin.
« Last Edit: October 09, 2019, 12:43:53 pm by Nimral »
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

howardpc

  • Hero Member
  • *****
  • Posts: 3144
Re: Inherited Constructor (add a Parameter)?
« Reply #9 on: October 09, 2019, 01:00:10 pm »
Handoko & Thaddy,

Handokos sample showed me the way, thanks!

I don't mean to waste your time, but trying to evolve from traditional Pascal to Object Pascal some background infos would be greatly appreciated.

This is Handokos routine, which works:

Code: Pascal  [Select]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   MyList: TMyList;
  4.   S: string;
  5. begin
  6.   S := 'one;two;three';
  7.   MyList := TMyList.Create(S);   // 1
  8.   Memo1.Lines.AddStrings(MyList);   // 2
  9.   MyList.Free;   // 3
  10. end;

What I did try to avoid was the lines 1 and 3, since I never actually intend to do something with MyList.

Code: Pascal  [Select]
  1. Memo2.Lines.AddStrings(MyList.Create(S));  // 4
  2. Memo2.Lines.AddStrings(TMyList.Create(S));  // 5
  3.  

OK, seems I cannot use (4), guess that's just because of the way the creators of Object Pascal decided their language should work, but why doesn't the compiler complain?

Line (5) works, but does cause a memory leak, since MyList.Free is never called. I considered this, but from what I was used to, variables declared as var and not beeing pointers with memory allocated manually (New/Dispose/GetMem/FreeMem) are automatically freed when their scope is exited. I did somehow expect that the same should happen to var MyList - the destructor should be called automatically. Why wasn't it?
Object Pascal has two different mechanisms for automatically freeing of entities allocated on the heap. One is via internal compiler management of a limited range of "managed types" such as ansistrings and dynamic arrays. You can reliably expect that all ansistrings and dynamic arrays you declare and use will be cleaned up for you by compiler-inserted code (code which you do not see unless you dump the assembly output from the compiler).
The other mechanism (introduced by the first developers of Delphi) applies to all classes that descend from TComponent. If you provide a non-Nil Owner in the constructor of any such component, the component's destruction will be handled automatically by the Owner via code you can examine in the FCL.
No other classes have an automatic destruction mechanism, i.e. the default for non-components such as TStringList is that you have to manage their destruction in code you yourself write.
Both these lines:
Code: Pascal  [Select]
  1. Memo2.Lines.AddStrings(MyList.Create(S));  // 4
  2. Memo2.Lines.AddStrings(TMyList.Create(S));  // 5
  3.  
invoke a valid TStringList constructor (Create), as Marco wrote,  but neither of them assigns a reference to the created instance to a variable that enables you to refer to that instance when it comes time to dispose of it. That reference (which is available in the constructor call) is thrown away at the moment of construction, in that you do not keep a reference to it. Consequently you create a memory leak.
Handoko's example saves a reference at the time of construction in a variable named MyList. This enables him to Free the instance once it has served its purpose.
« Last Edit: October 09, 2019, 01:03:26 pm by howardpc »

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: Inherited Constructor (add a Parameter)?
« Reply #10 on: October 09, 2019, 01:28:51 pm »
Thanks howard,

you have given excellent answers. I already wondered why some objects need to be destroyed manually, why others didn't.

May I bug you once more ...

Code: Pascal  [Select]
  1. MyList.Create(S));
  2. TMyList.Create(S));
  3.  

what's the difference? I think I already have grasped that, to instantiate an object, I need

Code: Pascal  [Select]
  1. var MyList:TMyList
  2.  
  3. ...
  4. MyList := TMyList.create()
  5.  

which seems to be the usual way to instantiate an object in Object Pascal. But it seems that

Code: Pascal  [Select]
  1. MyList.create()
  2.  

has some useful meaning/functionality too. Can you write a bit on that too? Why would I call the constructor of an already constructed object?

Thanks,

Armin.
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

howardpc

  • Hero Member
  • *****
  • Posts: 3144
Re: Inherited Constructor (add a Parameter)?
« Reply #11 on: October 09, 2019, 02:09:12 pm »
But it seems that

Code: Pascal  [Select]
  1. MyList.create()
  2.  

has some useful meaning/functionality too. Can you write a bit on that too? Why would I call the constructor of an already constructed object?
I don't think you should call a constructor twice using the same variable. That is bound to lead to a memory leak. And if MyList is Nil, then calling Create on it is nonsense.
Just because a bit of code compiles does not mean that it is therefore sensible or logically correct. You have to ask always "What is this code actually doing?"

The proper way to instantiate a class is to call its constructor using the name of its declared type, and to save a reference to that instance in a corresponding variable (or pass it to the Owner of the instance in those cases where there is an Owner).

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7430
Re: Inherited Constructor (add a Parameter)?
« Reply #12 on: October 09, 2019, 02:34:45 pm »

Because it is valid code. Just not what you want to do. 


Seems so :-) What then is the correct use of either? In other words - what's the difference between MyList.Create and TMyList.Create? When to use which one?
Armin.

mylist.create is only the constructor call and only used in rare cases where you (usualy mass-)allocate classes outside of the memory manager.  The same call but implicit (one constructor calling another from within the constructor) is more common.

TMylist.create is the complete constructor trajectory, memory allocation inclusive.

Handoko

  • Hero Member
  • *****
  • Posts: 3152
  • My goal: build my own game engine using Lazarus
Re: Inherited Constructor (add a Parameter)?
« Reply #13 on: October 09, 2019, 03:33:52 pm »
@Nimral

The word "object" is confusing. It can mean many things: a visual component that can be put on a form, a complex data type in OOP that is created on the stack, an instance of class, etc.

Object was introduced in Turbo Pascal, class was introduced in Delphi. Object still has its advantages but nowdays programmers usually use class. To answer your question you need to understand how to initialize an object and an instance of class.

MyList := TMyList.Create; // create an instance of class
MyList.Create; // initialize an object (that created on the stack, the TP version)

FYI, the constructor in the TP version usually named Init (not Create).

Read more:
https://wiki.freepascal.org/Programming_Using_Objects#Objects_-_Constructors_and_Destructors
« Last Edit: October 09, 2019, 03:35:27 pm by Handoko »

jamie

  • Hero Member
  • *****
  • Posts: 1980
Re: Inherited Constructor (add a Parameter)?
« Reply #14 on: October 09, 2019, 11:31:53 pm »
There are multiple ways to do what the poster wants,  most of them leads to memory leaks however, you can write a fixed function with a stringlist gets created at startup and used through out the app for the task, at the end you clean it up.

 So when all said and done...

Code: Pascal  [Select]
  1.  
  2. Function GetStringList(A:String):TStringList;
  3. Begin
  4.   result := AGlobeStringList;
  5.   result.Clear;
  6.   result.Add(A);
  7. end;            
  8. -------
  9.  
  10.  Amem.Assign(GetStringList('xfdsdsdsdfsfs'));
  11.  
  12. --    
  13.  

somewhere during startup and shutdown of program the GlobestringList needs to be managed.