Recent

Author Topic: Why should we use classes instead of records?  (Read 6142 times)

İbrahim

  • New Member
  • *
  • Posts: 19
Why should we use classes instead of records?
« on: August 20, 2018, 12:34:04 am »
Hi;

Actually I know that classes are creating on the heap and records are creating on the stack in Object Pascal.
Also I know that C++ classes are creating on the stack. My question actually is why does classes being used more than records in Object Pascal? I mean C++ programmers usually use static memory (stack) but as far as I know Object Pascal programmers usually use dynamic memory (heap).

Thanks.

VTwin

  • Hero Member
  • *****
  • Posts: 1215
  • Former Turbo Pascal 3 user
Re: Why should we use classes instead of records?
« Reply #1 on: August 20, 2018, 01:29:51 am »
Classes are fundamental to object oriented program. Are you wondering why people use classes instead of advanced records? They are more comparable.

Edit: I guess you are asking instead about the differences in C++ and Pascal memory allocation strategies for classes? I imagine someone will jump in with an answer.
« Last Edit: August 20, 2018, 01:38:42 am by VTwin »
“Talk is cheap. Show me the code.” -Linus Torvalds

Free Pascal Compiler 3.2.2
macOS 12.1: Lazarus 2.2.6 (64 bit Cocoa M1)
Ubuntu 18.04.3: Lazarus 2.2.6 (64 bit on VBox)
Windows 7 Pro SP1: Lazarus 2.2.6 (64 bit on VBox)

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Why should we use classes instead of records?
« Reply #2 on: August 20, 2018, 01:33:53 am »
Hi;

Actually I know that classes are creating on the heap and records are creating on the stack in Object Pascal.
Also I know that C++ classes are creating on the stack. My question actually is why does classes being used more than records in Object Pascal? I mean C++ programmers usually use static memory (stack) but as far as I know Object Pascal programmers usually use dynamic memory (heap).

Thanks.
polymorphism requires heap even on C/C++ objects.
Are you wondering why people use classes instead of advanced records? They are more comparable.
no inheritance, no comparison.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Why should we use classes instead of records?
« Reply #3 on: August 20, 2018, 02:52:25 am »
Actually I know that classes are creating on the heap and records are creating on the stack in Object Pascal.

Hmmm... Actually that depends on quite a lot of circumstances: where you declare the classes/objects/records, how you use them, etc. For example, in this code MyRecord is *not* created in the stack.

Code: Pascal  [Select][+][-]
  1. program toto;
  2.  
  3. type
  4.   TMyRecord: packed record
  5.     Id: Integer;
  6.     Code: AnsiChar;
  7.     Description: ShortString
  8.   end;
  9.  
  10. var
  11.   MyRecord: TMyRecord;
  12.  
  13. begin
  14.   MyRecord.Id := 19654;
  15.   MyRecord.Code := 'A';
  16.   MyRecord.Description := 'An absurd do-nothing example';
  17.   WriteLn('We did do-nothing!!!');
  18. end.
  19.  

And AFAIK (which isn't much, truth be told) the same happens in C(++).

As for why we should use classes instead of records ... we shouldn't. We should use the most appropriate construct for each piece of code. No more, no less  ;D

Quote
[...] C++ programmers usually use static memory (stack) but as far as I know Object Pascal programmers usually use dynamic memory (heap).

Huh? No, not really. At least in the code bases I have seen. Both camps (try to) use the appropriate construct and let the compiler decide how memory allocation is done, but minimally large C(++) code is usually choked full of calls to functions of the alloc family ... which gets its memory from the heap. Conversely, Pascal programmers tend to divide their code into more, smaller procedures/functions, which uses (generally) the stack. So it really is a mixed kind of situation for both.

---
Edited to add the second part.
« Last Edit: August 20, 2018, 03:06:34 am by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Why should we use classes instead of records?
« Reply #4 on: August 20, 2018, 05:16:06 am »
Actually, old school objects are closer to how stack oriented C++ classes work.
Also note that C++ classes can be just as heap oriented as Object Pascal classes and in some serious application in libraries it is even preferred. (If you see a lot of -> it is likely heap  8-))
An example of this is how C++Builder's non-standard libraries work (not only the delphi bindings). Another one is COM/XCOM (in C++ too:heap)

So really it is all a bit of confusion:
- C++ Classes are not necessary stack oriented, it depends on how they are written.
- Anything that has virtual methods or reference counted types (or invokes structured exception handling) or requires multi-threading application uses some heap anyway. C++ or Object Pascal.
- Stack oriented classes from C++ resemble old school Object Pascal Objects, which also favor stack over heap, depending on use, but see the above.
- If the class's constructor is invoked through new, assume it is heap, unless you know new is overridden to use stackalloc. On old school objects: when created with new and dispose, it is likely heap.
Side note: there exists some Object Pascal code that has stack based memory management for dynamically created old school objects too. an example can be found in the implementation section of Delphi 3/4/5/6/7+ grids.pas unit for cell objects management. It is well hidden there, for obvious reasons... :D It was factored out into a mm and published in UNDU.
Ref: http://rmarsh.com/2005/08/28/objects-on-the-stack-a-delphi-relic/ That article is old, but very enlightening and a must read.


Also note using stack has some advantages for smaller classes (e.g. speed through automatic destruction at end of scope) it has also many disadvantages: e.g. stack size management, debugging, exception handling and thread-safety.
To summarize:
-  C++ classes are NOT necessarily stack oriented. But you have the option to write them that way but that also limits their application.
-  In object Pascal we have the option of using old school objects in a stack oriented way, not only records. Same limitation.
-  In both languages choosing the stack requires a lot of thought. Mixing stack and heap is actually not for beginners in both languages.
- The statement: C++ classes are stack oriented as a generalization is false.

note: the code from the link is not FreePascal ready and requires a small rewrite, because of differing memory manager structures in Delphi vs Freepascal.

(For user 440x: if you indeed like to factor out any reference counted types and use shortstrings, this may be a little toy for you  :D ;D)
« Last Edit: August 20, 2018, 06:25:15 am by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Why should we use classes instead of records?
« Reply #5 on: August 20, 2018, 06:35:25 am »
The above became quite long but I would like to add the following observation:
Even with normal Object Pascal classes it is possible - within certain limitations! - to use a stack based  memory manager like mentioned above.
This requires you to override NewInstance and FreeInstance where you should allocate on the stack. FreeInstance should do nothing much.
- don't use reference counted types
- use shortstringsNo strings at all. Use PChar.
- use preferably only static - non-virtual - methods (Although overriding NewInstance to use stackalloc puts a vmt record also on the stack..., we probably need to copy that original vmt, careful what you wish for...); because declaring the class is enough to have a pointer to a vmt (besides we need that for our instance size: NewInstance uses PVmt(self)^.vInstanceSize, not TStackObject(self) which is just a native pointer). I need to work around that.
You can use virtual/override.
- Your class hierarchy needs to be derived from your own TStackObject class. The code is also FPC version dependent and not portable.
basically:
Code: Pascal  [Select][+][-]
  1. type
  2.   TStackObject = class
  3.   public
  4.     class function NewInstance:Tobject;override;  // calls StackAlloc Tobject needs to be TStackObject! then maybe initinstance.
  5.     procedure  FreeInstance;override;  // (cleans up, dunno what) // Does basically nothing. Auto free when out of scope.
  6.   end;

Don't try this at home and don't use virtual methods at all.  Furthermore all destructors must probably be called in the exact reverse order of all creates!   
Further furthermore: don't use structured exception handling or try finally on those TStackObjects!. That will corrupt the stack (AllocA does the same in C, btw)
I will maybe add a small example later today...
« Last Edit: August 20, 2018, 11:31:12 am by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Why should we use classes instead of records?
« Reply #6 on: August 20, 2018, 09:53:23 am »
Ok. I seem to have succeeded (careful! do some tests first!!! Use at your own risk!!)
It appears Object Pascal classes CAN be on the stack!!! Yeey!
Code: Pascal  [Select][+][-]
  1. program stackbasedclasses;
  2. {$ifdef fpc}{$mode delphi}{$H-}{$S+}{$endif}
  3. {$ifndef win64}{$error this is for windows x86_64 only!}{$endif}
  4. {$L allocafunc64.obj}
  5. function _alloca(theSize: integer; alignm: integer; accumPtr: pointer = nil): pointer;
  6.   external Name '_alloca';
  7.  
  8. type
  9.   TStackObject = class
  10.   public
  11.      class function newinstance : tobject;override;
  12.      procedure FreeInstance;override;
  13.      procedure show;virtual;
  14.   end;
  15.  
  16.   TStackObject2 = class(TStackObject)
  17.   public
  18.     procedure Show;override;
  19.   end;
  20.      
  21.   class function TStackObject.NewInstance:Tobject;
  22.   var
  23.     p : pointer;
  24.   begin
  25.     p := _alloca(InstanceSize,4);  // allocate on the stack. 4 byte aligned
  26.     if p <> nil then
  27.       InitInstance(p);
  28.     NewInstance:=TObject(p);
  29.   end;
  30.  
  31.   procedure TStackObject.FreeInstance;
  32.   begin
  33.     // CleanUpInstance; // not nessesary
  34.     // no free!
  35.   end;
  36.  
  37.   procedure TStackObject.Show;
  38.   begin
  39.     writeln(self.Classname);
  40.   end;
  41.  
  42.   procedure TStackObject2.Show;
  43.   begin
  44.     writeln(self.Classname);
  45.   end;
  46.  
  47.  
  48. var
  49.  testme,testme2:TStackObject;  
  50. begin
  51.  testme := TStackObject.Create;
  52.  testme.show;
  53.  testme2 := TStackObject2.Create;
  54.  Testme2.Show;
  55. end.
I attached the obj file.
When you compile this with ppcx64 -glh the output looks like this:
Code: Bash  [Select][+][-]
  1. J:\alloca>stackbasedclasses
  2. TStackObject
  3. TStackObject2
  4. Heap dump by heaptrc unit of J:\alloca\stackbasedclasses.exe
  5. 0 memory blocks allocated : 0/0
  6. 0 memory blocks freed     : 0/0
  7. 0 unfreed memory blocks : 0
  8. True heap size : 65536 (160 used in System startup)
  9. True free heap : 65376
  10.  

See? No heap memory allocated *at all*.
I did not write the alloca asm code myself. I will add a link later.
The alloca code is from a fellow FPC user here: http://www.atelierweb.com/64-bit-_alloca-how-to-use-from-delphi/ although he probably didn't realize that you can use it like this  8-) :D
(No clue, but reveal yourself if you are reading this)
Its application, the concept for using it in this way is mine, though  :D :o

Be careful. It is only marginally tested. Don't use any strings as fields. PChar seems to work.
TStackObjects as fields seem OK, as do all simple types, static arrays and records. Any other Tobject or reference counted types, of course not!

Please test! and report back....


« Last Edit: August 20, 2018, 06:00:42 pm by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Why should we use classes instead of records?
« Reply #7 on: August 20, 2018, 10:36:01 am »
I will add a unit form later.

In unit form:
Code: Pascal  [Select][+][-]
  1. { This is a unit that enables classes to be allocated on the stack.
  2.   There are several constraints:
  3.   1. Stack based classes must inherit from TStackobject
  4.   2. You can not use fields that are managed types:
  5.      - No strings, not even shortstrings
  6.      - No inner classes
  7.      - No interfaces
  8.      - No dynamic arrays
  9.      - No advanced records
  10.      - etc. Again, no managed types!
  11.   3. You can use  fields of type:
  12.      - All simple types
  13.      - PChar
  14.      - Simple Records
  15.      - Static arrays
  16.    
  17.    This unit was written by thaddy de koning (C) 2018.
  18.    Use as you like, but use at your own risk.
  19.    You have been warned!
  20.    Credits for alloca: http://www.atelierweb.com/64-bit-_alloca-how-to-use-from-delphi/ }
  21.    
  22. unit ClassOnStack;
  23. {$MODE DELPHI}{$H-}{$IFOPT D+}{$S+}{$ENDIF}{$O-}
  24. {$L allocafunc64.obj}
  25. interface
  26.  
  27. function _alloca(theSize: integer; alignm: integer; accumPtr: pointer = nil): pointer;
  28.   external Name '_alloca';
  29. { You can enable this to have normal heap allocation }
  30. {.$DEFINE NOSTACK}
  31. type
  32.   TStackObject = class
  33.   public
  34.   {$IFNDEF NOSTACK}
  35.      class function newinstance :tobject;override;
  36.      procedure FreeInstance;override;
  37.   {$ENDIF}
  38.   end;
  39.  
  40. implementation
  41.  
  42.   {$IFNDEF NOSTACK}  
  43.   class function TStackObject.NewInstance:Tobject;
  44.   var
  45.     p : pointer;
  46.   begin
  47.     p := _alloca(InstanceSize,4);  // allocate on the stack. 4 byte aligned
  48.     if p <> nil then
  49.       InitInstance(p);
  50.     NewInstance:=TObject(p);
  51.   end;
  52.  
  53.   procedure TStackObject.FreeInstance;
  54.   begin
  55.     // this is actually not necessary: you don't need to call free
  56.     // CleanUpInstance;
  57.   end;
  58.   {$ENDIF}
  59. end.
« Last Edit: August 20, 2018, 05:59:04 pm by Thaddy »
Specialize a type, not a var.

İbrahim

  • New Member
  • *
  • Posts: 19
Re: Why should we use classes instead of records?
« Reply #8 on: August 20, 2018, 03:23:21 pm »
Thanks for all your replies.

Quote
Thaddy: C++ classes are stack oriented as a generalization is false.
Default C++ classes are value typed (created on the stack). Default Object Pascal classes are reference typed (created on the heap). I mean:
Code: Diff  [Select][+][-]
  1. class Example
  2. {
  3. private:
  4.   int _count;
  5. public:
  6.   Example();
  7.   Example(int count);
  8.   void print() const;
  9. };
  10.  
  11. Example::Example() : _count(0)
  12. {
  13. }
  14.  
  15. Example::Example(int count) : _count(count)
  16. {
  17. }
  18.  
  19. void Example::print() const
  20. {
  21.   std::cout << _count << std::endl;
  22. }
  23.  
  24. void main()
  25. {
  26.   // Default:
  27.   Example instance(5); // Value typed, created on the stack.
  28.   instance.print();
  29.  
  30.   // Dynamic:
  31.   Example* instance_ptr = new Example(5); // Reference typed, created on the heap.
  32.   instance_ptr->print();
  33.   // We must to delete instance_ptr like Object Pascal default classes:
  34.   delete instance_ptr;
  35. }
  36.  

In Object Pascal:
Code: Pascal  [Select][+][-]
  1. type
  2. Example = class
  3. private
  4.   _Count : Integer;
  5. public
  6.   constructor Create; overload;
  7.   constructor Create(Count: Integer); overload;
  8.   procedure Print;
  9. end;
  10.  
  11. /////////////////////
  12. RecordExample = record;
  13. private
  14.   _Count : Integer;
  15. public
  16.   class function Create : RecordExample; overload;
  17.   class function Create(Count: Integer) : RecordExample; overload;
  18.   procedure Print;
  19. end;
  20. /////////////////////
  21.  
  22.   constructor Example.Create; overload;
  23.   begin
  24.     _Count := 0;
  25.   end;
  26.  
  27.   constructor Example.Create(Count: Integer); overload;
  28.   begin
  29.     _Count := Count;
  30.   end;
  31.  
  32.   procedure Example.Print;
  33.   begin
  34.     WriteLn(_Count);
  35.   end;
  36.  
  37. var
  38.   Instance : Example;
  39.   Instance2 : RecordExample;
  40. begin
  41.   Instance := Example.Create(5); // Reference typed, created on the heap.
  42.   try
  43.     Instance.Print;
  44.   finally
  45.     // We must to delete instance in OP.
  46.     FreeAndNil(Instance);
  47.   end;
  48.  
  49.   Instance2 := RecordExample.Create(5); // Value typed, created on the stack.
  50.   Instance2.Print;
  51. end.
  52.  

I'm wondering that why are default C++ classes value typed, not reference typed but Object Pascal classes are reference typed, not value typed? Should a class be value typed or reference type as default in any programming language? Actually my question is this.

I'm usually writting C++ codes and I use C++ object files in OP.
There is a C++ class:
Code: Diff  [Select][+][-]
  1. class QString
  2. {
  3. private:
  4.  ...
  5. public:
  6.  ...
  7. };
  8.  
I want to use this QString C++ class in Object Pascal. Should I use this:
Code: Pascal  [Select][+][-]
  1. type
  2.   QString = record
  3.   end;
  4.  
Or should I use this:
Code: Pascal  [Select][+][-]
  1. type
  2.   QString = class
  3.   end;
  4.  
And why? Thanks all.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Why should we use classes instead of records?
« Reply #9 on: August 20, 2018, 03:28:13 pm »
Quote
Code: C  [Select][+][-]
  1.   // Dynamic:
  2.   Example* instance_ptr = new Example(5); // Reference typed, created on the heap.
  3.  
Is exactly what I described. before, isn't it? The statement that C++ classes are always created on the stack is not true.

Anyway, I found a - somewhat limited - way to create Object Pascal classes on the stack. See above. Note I use C++ more than I like and for over 20 years.
« Last Edit: August 20, 2018, 03:32:10 pm by Thaddy »
Specialize a type, not a var.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Why should we use classes instead of records?
« Reply #10 on: August 20, 2018, 03:41:27 pm »
I'm wondering that why are default C++ classes value typed, not reference typed but Object Pascal classes are reference typed, not value typed? Should a class be value typed or reference type as default in any programming language?
I guess they took different approaches to solving the same problem as thaddy already said turbo pascal 5 or 6 had similar objects with C/C++ with delphi 1.0 they moved to heap based classes instead I never managed to find any info on why they did that I have some assumptions on why but they are only that assumptions and not worth sharing.
Actually my question is this.

I'm usually writting C++ codes and I use C++ object files in OP.
There is a C++ class:
Code: Diff  [Select][+][-]
  1. class QString
  2. {
  3. private:
  4.  ...
  5. public:
  6.  ...
  7. };
  8.  
I want to use this QString C++ class in Object Pascal. Should I use this:
Code: Pascal  [Select][+][-]
  1. type
  2.   QString = record
  3.   end;
  4.  
Or should I use this:
Code: Pascal  [Select][+][-]
  1. type
  2.   QString = class
  3.   end;
  4.  
And why? Thanks all.
neither is safe again quoting Thaddy you should use Object eg
Code: Pascal  [Select][+][-]
  1. type
  2.   QString = object
  3.   end;
  4.  
but not before reading this http://rvelthuis.de/articles/articles-cppobjs.html it should give you a general idea of what problems to expect.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Why should we use classes instead of records?
« Reply #11 on: August 20, 2018, 04:04:07 pm »
Well, 99% of the time the objects are dynamically allocated.  Making classes an auto reference removes the need to constantly mess with pointer syntax and have special parameters modifiers like & in C++.

Classes this way also don't need a deep assignment operation, since normal assignment just copies references.

For iterators, and other small potato stuff (like TPoint and vector types) that is often static, there's records with methods.

So the only problems are combining that, but not having to deal with the pointer syntax most of the time is well worth that.

 

TinyPortal © 2005-2018