0% found this document useful (0 votes)
6 views

Extra 3

Uploaded by

skamelrech2020
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Extra 3

Uploaded by

skamelrech2020
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 4

138 - 05: Arrays and Records

Records with Methods


In Object Pascal records are more powerful than in the original Pascal language or
than structs are in the C language. Records, in fact, can have procedure and func-
tions (called methods) associated with them. They can even redefine the language
operators in custom ways (a feature called operator overloading), as you'll see in
the next section.
A record with methods is somewhat similar to a class, as we'll find out later, with the
most important difference being the way these two structures manage memory.
Records in Object Pascal have two fundamental features of modern programming
languages:
• Methods, which are functions and procedures connected with the record data
structure and having direct access to the record fields. In other words, methods
are function and procedures declared (or having a forward declaration) within
the record type definition.
• Encapsulation, which is the ability to hide direct access to some of the fields (or
methods) of a data structure from the rest of the code. You can obtain encapsula-
tion using the private access specifier, while fields and methods visible to the
outside as marked as public. The default access specifier for a record is public.
Now that you have the core concepts around extended records, let's look at the defi-
nition of a sample record, taken from the RecordMethods application project:
type
TMyRecord = record
private
Name: string;
Value: Integer;
SomeChar: Char;
public
procedure Print;
procedure SetValue (NewString: string);
procedure Init (NewValue: Integer);
end;
You can see the record structure is divided in two parts, private and public. You can
have multiple sections, as the private and public keywords can be repeated as many
times as you want, but a clear division of these two sections certainly helps readabil-
ity. The methods are listed in the record definition (like in a class definition) without
their complete code. In other words, the record has a forward declaration of the
method.
How do you write the actual code of a method, its complete definition? Almost in the
same way you'd code a global function or procedure. The differences are in the way

Marco Cantù, Object Pascal Handbook


05: Arrays and Records - 139

you write the method name, which is a combination of the record type name and the
actual record name and on the fact you can directly refer to the fields and the other
methods of the record directly, with no need to write the name of the record:
procedure TMyRecord.SetValue (NewString: string);
begin
Name := NewString;
end;

note While it might seem tedious having to write the definition of the method first and its full declara-
tion next, you can use the Ctrl+Shift+C combination in the IDE editor to generate one from the
other automatically. Also you can use the Ctrl+Shift+Up/Down Arrow keys to move from a
method declaration to the corresponding definition and vice verse.

Here is the code of the other methods of this record type:


procedure TMyRecord.Init(NewValue: Integer);
begin
Value := NewValue;
SomeChar := 'A';
end;

function TMyRecord.ToString: string;


begin
Result := Name + ' [' + SomeChar + ']: ' + Value.ToString;
end;
Here is a sample snippet of how you can use this record:
var
MyRec: TMyRecord;
begin
MyRec.Init(10);
MyRec.SetValue ('hello');
Show (MyRec.ToString);
As you might have guessed, the output will be:
hello [A]: 10
Now what if you want to use the fields from the code that uses the record, like in the
snippet above:
MyRec.Value := 20;
This actually compiles and works, which might be surprising as we declared the field
in the private section, so that only the record methods can access to it. The truth is
that in Object Pascal the private access specifier is actually enabled only between
different units, so that line wouldn't be legal in a different unit, but can be used in
the unit that originally defined the data type. As we will see, this is also true for
classes.

Marco Cantù, Object Pascal Handbook


140 - 05: Arrays and Records

Self: The Magic Behind Records


Suppose you have two records, like myrec1 and myrec2 of the same record type.
When you call a method and execute its code, how does the method know which of
the two copies of the record it has to work with? Behind the scenes, when you define
a method the compiler adds a hidden parameter to it, a reference to the record you
have applied the method to.
In other words, the call to the method above is converted by the compiler in some-
thing like:
// you write
MyRec.SetValue ('hello');

// the compiler generates


SetValue (@MyRec, 'hello');
In this pseudo code, the @ is the address of operator, used to get the memory loca-
tion of a record instance.

note Again, the address of operator is shortly covered at the end of this chapter in the (advanced) sec-
tion titled “What About Pointers?”

This is how the calling code is translated, but how can the actual method call refer
and use this hidden parameter? By implicitly using a special keyword called self. So
the method's code could be written as:
procedure TMyRecord.SetValue (NewString: string);
begin
self.Name := NewString;
end;
While this code compiles, it makes little sense to use self explicitly, unless you need
to refer to the record as a whole, for example passing the record as parameter to
another function. This happens more frequently with classes, which have the same
exact hidden parameter for methods and the same self keyword.
One situation in which using an explicit self parameter can make the code more
readable (even if it is not required) is when you are manipulating a second data
structure of the same type, as in case you are testing a value from another instance:
function TMyRecord.IsSameName (ARecord: TMyRecord): Boolean;
begin
Result := (self.Name = ARecord.Name);
end;

note The “hidden” self parameter is called this in C++ and Java, but it is called self in Objective-C
(and in Object Pascal, of course).

Marco Cantù, Object Pascal Handbook


05: Arrays and Records - 141

Records and Constructors


When you define a variable of a record type (or a record instance) as a global vari-
able its fields are initialized, but when you define one on the stack (as a local variable
of a function or procedure, it isn't). So if you write code like this (also part of the
RecordMethods project):
var
MyRec: TMyRecord;
begin
Show (MyRec.ToString);
its output will be more or less random. While the string is initialized to an empty
string, the character field and the integer field will have the data that happened to be
at the given memory location (just as it happens in general for a character or integer
variable on the stack). In general, you'd get different output depending on the actual
compilation or execution, such as:
[!]: 1637580
That's why it is important to initialize a record (as most other variables) before using
it, to avoid the risk of reading illogical data, which can even potentially crash the
application.
Records support a special type of methods called constructors, that you can use to
initialize the record data. Differently from other methods, constructors can also be
applied to a record type to define a new instance (but they can still be applied to an
existing instance).
This is how you can add a constructor to a record:
type
TMyNewRecord = record
private
...
public
constructor Create (NewString: string);
function ToString: string;
...
The constructor is a method with code:
constructor TMyNewRecord.Create (NewString: string);
begin
Name := NewString;
Init (0);
end;
Now you can initialize a record with either of the two following coding styles:
var
MyRec, MyRec2: TMyNewRecord;
begin

Marco Cantù, Object Pascal Handbook

You might also like