Records in Delphi
Asked Answered
D

5

17

some questions about records in Delphi:

  1. As records are almost like classes, why not use only classes instead of records?
  2. In theory, memory is allocated for a record when it is declared by a variable; but, and how is memory released after?
  3. I can understand the utility of pointers to records into a list object, but with Generics Containers (TList<T>), are there need to use pointer yet? if not, how to delete/release each record into a Generic Container? If I wanna delete a specific record into a Generic Container, how to do it?
Dunlavy answered 9/12, 2009 at 21:6 Comment(0)
D
12

For 1 and 2: records are value types, while classes are reference types. They're allocated on the stack, or directly in the memory space of any larger variable that contains them, instead of through a pointer, and automatically cleaned up by the compiler when they go out of scope.

As for your third question, a TList<TMyRecord> internally declares an array of TMyRecord for storage space. All the records in it will be cleaned up when the list is destroyed. If you want to delete a specific one, use the Delete method to delete by index, or the Remove method to find and delete. But be aware that since it's a value type, everything you do will be making copies of the record, not copying references to it.

Darmit answered 9/12, 2009 at 21:16 Comment(2)
Thanks Mr. Wheeler, and one question more: Pointer to record = Class ?Dunlavy
No, pointer to record = pointer to record. An object (class instance) is different from a record in several ways.Darmit
M
22

There are lots of differences between records and classes; and no "Pointer to record" <> "Class". Each has its own pros and cons; one of the important things about software development is to understand these so you can more easily choose the most appropriate for a given situation.

  1. This question is based on a false premise. Records are not almost like classes, in the same way that Integers are not almost like Doubles.
    • Classes must always be dynamically instantiated, whereas this is a possibility, but not a requirement for records.
    • Instances of classes (which we call objects) are always passed around by reference, meaning that multiple sections of code will share and act on the same instance. This is something important to remember, because you may unintentionally modify an object as a side-effect; although when done intentionally it's a powerful feature. Records on the other hand are passed by value; you need to explicitly indicate if you're passing them by reference.
    • Classes do not 'copy as easily as records'. When I say copy, I mean a separate instance duplicating a source. (This should be obvious in light of the value/reference comment above).
    • Records tend to work very nicely with typed files (because they're so easy to copy).
    • Records can overlay fields with other fields (case x of/unions)
    • These were comments on certain situational benefits of records; conversely, there are also situational benefits for classes that I'll not elaborate on.
  2. Perhaps the easiest way to understand this is to be a little pedantic about it. Let's clarify; memory is not really allocated 'when its declared', it's allocated when the variable is in scope, and deallocated when it goes out of scope. So for a local variable, it's allocated just before the start of the routine, and deallocated just after the end. For a class field, it's allocated when the object is created, and deallocated when it's destroyed.
  3. Again, there are pros and cons...
    • It can be slower and require more memory to copy entire records (as with generics) than to just copy the references.
    • Passing records around by reference (using pointers) is a powerful technique whereby you can easily have something else modify your copy of the record. Without this, you'd have to pass your record by value (i.e. copy it) receive the changed record as a result, copy it again to your own structures.
  4. Are pointers to records like classes? No, not at all. Just two of the differences:
    • Classes support polymorphic inheritance.
    • Classes can implement interfaces.
Middleclass answered 9/12, 2009 at 23:47 Comment(2)
Thanks Marco for adding the note about variant records (using case x of...)Middleclass
Side dish: FastMM preallocates some memory for your program. If you create a bunch of small records/objects/whatever, it is VERY likely your program will not request memory from the OS. So, we could say that for small structures (variable, strings, records, basic objects), there is no significant speed difference if the structure is placed on heap or on the stack.Donner
D
12

For 1 and 2: records are value types, while classes are reference types. They're allocated on the stack, or directly in the memory space of any larger variable that contains them, instead of through a pointer, and automatically cleaned up by the compiler when they go out of scope.

As for your third question, a TList<TMyRecord> internally declares an array of TMyRecord for storage space. All the records in it will be cleaned up when the list is destroyed. If you want to delete a specific one, use the Delete method to delete by index, or the Remove method to find and delete. But be aware that since it's a value type, everything you do will be making copies of the record, not copying references to it.

Darmit answered 9/12, 2009 at 21:16 Comment(2)
Thanks Mr. Wheeler, and one question more: Pointer to record = Class ?Dunlavy
No, pointer to record = pointer to record. An object (class instance) is different from a record in several ways.Darmit
G
9

One of the main benefits of records is, when you have a large "array of record". This is created in memory by allocating space for all records in one contiguous RAM space, which is extremely fast. If you had used "array of TClass" instead, each object in the array would have to be allocated by itself, which is slow.

There has been a lot of work to improve the speed of allocating memory, in order to improve the speed of strings and objects, but it will never be as fast as replacing 100,000 memory allocations with 1 memory allocation.

However, if you use array of record, don't copy the record around in local variables. That may easily kill the speed benefit.

Grazing answered 10/12, 2009 at 6:45 Comment(2)
"by allocating space for all records in one contiguous RAM space, which is extremely fast" - Please accept my rant about this block allocation. Indeed, it will be faster to allocate many records in one block, however, if your array is large, your program (especially under 32bit compiler) might run out of memory EVEN if enough memory is available, because it will have troubles finding a large CONTIGUOUS block to fit the array. On the other side, if you use array of objects, the objects will be scattered all over in RAM, so fewer problems with the fragmentation.Donner
One of our uses cases is: An array of what to do for each pixel horizontally on the screen in a charting module. That is currently limited to around 8000 pixels, i.e. 8000 array indexes, and the algorithms traverse this several times. This can be allocated, processed and deallocated in just a few milliseconds, which is great. You couldn't do that equally fast with classes. It allocates less than 1MB RAM.Grazing
D
5

1) To allow for inheritance and polymorphism, classes have some overhead. Records do not allow them, and in some situations may be somewhat faster and simpler to use. Unlike classes, that are always allocated in the heap and managed through references, records can be allocated on the stack also, accessed directly, and assigned each other without requiring to call an "Assign" method. Also records are useful to access memory blocks with a given structure, because their memory layout is exactly how you define it. A class instance memory layout is controlled by the compiler and has additional data to make objects work (i.e. the pointer to the Virtual Method Table).

2) Unless you allocate records dynamically, using New() or GetMem(), record's memory is managed by the compiler as ordinals, floats or static arrays: global variables memory is allocated at startup and released when the program terminates, and local variables are allocated on the stack entering a function/procedure/method and released exiting. Allocating/releasing memory in the stack is faster because it doesn't require calls to the memory manager, it's just very few assembler instructions to change the stack registers. But be aware that allocating large structure on the stack may cause a stack overflow, because the maximum stack size is fixed and not very large (see linker options). If records are fields of a class, they are allocated when the class is created and released when the class is freed.

3) One of the advantages of generics is to eliminate the need of low-level pointer management - but be aware of the inner workings.

Dichroscope answered 10/12, 2009 at 8:52 Comment(2)
"Allocating/releasing memory in the stack is faster because it doesn't require calls to the memory manager" - Let's not forget that FastMM preallocates some memory for your program. If you create a bunch of small records/objects/whatever, it is VERY likely your program will not request memory from the OS. So, we could say that for small structures (strings, records, basic objects), there is no significant difference if the structure is placed in heap or stack.Donner
"Allocating/releasing memory in the stack is faster" - Hint: because FastMM pre-allocates memory, creating small records/variable in the heap is not much slower than creating them on the stack. This also leads to some waste: if you declare a 1 byte (boolean) global variable, 32 bytes will be allocated for it. ebookreading.net/view/book/EB9781788625456_75.htmlDonner
P
3

There are a few other differences between a class and a record. Classes can use polymorphism, and expose interfaces. Records can not implement destructors (although since Delphi 2006 they can now implement constructors and methods).

Records are very useful in segmenting memory into a more logical structure since the first data item in the record is at the same address point of the pointer to the record itself. This is not the case for classes.

Piscina answered 9/12, 2009 at 23:21 Comment(1)
Also, records support operator overloading, which can be useful in some situations.Prang

© 2022 - 2024 — McMap. All rights reserved.