Translating C++ Code Into Delphi
Transcript of Translating C++ Code Into Delphi
-
7/30/2019 Translating C++ Code Into Delphi
1/46
Translating C++ Code into Delphi Pascal
Because Microsoft chose C++ as the language of Windows programming API and C++ is
typically used in Unix and Linux programming systems, most books and many online
resources present code written in C++ that can be very enigmatic to Delphi programmers. For
commericial reasons C++ continues to be the principle language of discourse in the
programming community, despite the fact that it based on ancient technology and the earliest
attempts to build computer languages and compilers, and retains all of its obsolete syntax as a
result. (Another superior language, which incorporates decades of research into language and
compiler developement is Eiffel, which is simple, the new paradigm, even stricter than Delphi
in many ways, and yet very powerful due to both its simplified syntax and the optimized code
that results. The end result of compiler and programming research over a period of almost
forty years is that a CPU with simple instructions and a compiler and language that is
simplified results in superior execution speeds, with a RISC (Reduced Instruction Set
Computer) always beating an Intel or Unix computer in performance for the same CPU speed.
Nevertheless C++ has the weight of historical precedence behind it, even if it is not the best
language, as all those bugs, buffer over runs, and security patches testify. For all these
reasons, I have found it neccesary to understand C++ code, even if I don't plan to write muchof it myself, (prefering myself to learn Fortran 90 or Eiffel instead as additonal programming
languages) and so I have begun to research the syntax and organization of C++ code, and the
Delphi equivalents, and will be posting the results on this page.
As I encounter more enigmas in C++ code and interpret them, these pages will be updated
accordingly (my goal is to be able to read and translate C++ code). Some of the pages below
rely on what is discussed on other pages (for example there is a page on pointers, but pointers
are also discussed in the section on structures. I have tried to organize this material logically,
but you might find it useful to jump back and forth a couple of times between pages, if you
are unfamiliar with some of the features of C++)
C++ Operators
Scope of variables in C++
If - then - else structures in C++
The Switch statement
For, while and do loops in C++
Structures - records in C++
C++ pointers, the stack and the heap
Passing variables by reference and by value
C++ File I/O
C++ header interface files
Classes and inheritance
-
7/30/2019 Translating C++ Code Into Delphi
2/46
In addition to the discussion of C++ on this page, I have also included some examples of
programs translated from C++ source code to Delphi to illustrate some of the points made in
the articles linked to from this index page, and I post links to them below as they appear.
-
7/30/2019 Translating C++ Code Into Delphi
3/46
Understanding C++ OperatorsTranslating C++ Code into Delphi Pascal
C++ has five binary arithmetic operators, which take two operands and two unary
arithmetic operators, which can take one operand. Five assignment operators can be created
by combining these arithmetic operators with the equals sign to create 'assignment operators'.
The arithmetic operators are +, -, *, /, %, with the operators + and - having unary versions
which require only one operand. the assignment operators are +=, -+, *=, /+, and %=.
In Delphi the assignment operator is ' := ' while in C++ the assigment operator is simply ' =
'. This would be the equivalency operator in Delphi, while in C++ the equivalency operator is
' == '. So in Delphi we would write ' if x = 16 then ' and in C++ this would be written as ' if
(x==16) '. In Delphi we would write ' x := y ' which sets x equal to y, and in C++ we drop the
colon the perform the same operation ... ' x = y'.
The other relational operators are similar in both languages. They are , =, == and
then in Delphi where we write ' ' (not equal) in C++ this becomes ' != '.
The logical operators in C++ representing a logical AND, OR, and NOT as applied to
boolean operands are ' && ', ' || ', and ' ! '. There are also two logical assigment operators, '
&= ' and ' |= ' which perform and assign the results of a logical AND and a logical OR
operation respectively. For example,
x &= 2;
This would be similar to writing ' x := x AND 2; '
One of the differences in a C++ logical test is that if the first test fails the second test maynot be evaluated. To accomplish the same thing in Delphi requires nested if - then statements
(there is also a compiler option to turn off 'full boolean evaluation').
For example the following statement would generate an error in Delphi in the second part
of the statement if Delphi is doing full boolean evaluation and x is equal to zero, due to an
attempted division by zero...
if (x = 0) or (100 div x < 10) then
In C++ this statement would not generate an error, since, if x was equal to zero the test
would be considered a success and the other conditional statements need not be considered...
if (x==0 || 100 / x < 10)
Similarly in the above example a test to ensure that x is not zero would not cause a crash if
the or relational operator was replaced by an 'and' relational operator.
In C++ you might see operators and operands combined in the following ways...
++x;
This is similar to writing inc(x) in Delphi, which means x := x + 1 (the difference being thatthe former generates more optimized code.
-
7/30/2019 Translating C++ Code Into Delphi
4/46
The increment operator can appear before or after the operand...
y = ++x;
y = x++;
In the first cause (pre-increment) x is incremented by one, and the result is then assigned to
y. IN the second case, x is assigned to y and then x is incremented by one.
In Delphi we could write...
y := x; inc(x);
And in C++ this becomes...
y = x++;
An example of using an assignment operator in C++ ...
x /= z;
In this case two operands (x and z) are combined using an assignment operator. This
statement could also be written as ' x = x / z; '
Assignment operators can also be evaluated from right to left in a statement resulting in
strange looking calculations like the following.
x = y *= 10;
This is equivalent to following code...
y = y * 10; x = y;
When arithemetic operators are included multiple times in a single line the operators are
evaluated from left to right if they have the same precedence, otherwise the operator with the
highest precedence is evaluated first. One way to get around memorizing the precedence of
operators is to enforce precedence using brackets, in which case the rule is that the inner most
bracket is evaluated first, then so on until the outer most bracket is evaluated. For example,
consider the following line of code without brackets.
x = y + z - q * 6 / t;
Both mulitiplication and division have a greater precedence than adding or substracting,
and multiplication ' * ' and division ' / ' both have equal precedence so evaluating from left to
right we get first q * 6 and then the result is divided by t. Then once again working from the
left we get y + z evaluated and then the previous results of the multiplication and division are
subtracted from this result and the solution is then assigned to the variable x. A change in
precedence causes a change in results. One way to ensure that the results are what you want
is to use brackets.
-
7/30/2019 Translating C++ Code Into Delphi
5/46
Let us suppose that first q should be subtracted from z and 6 should be divided by t and
then these two results should multiplied together and then added to y. To override precedence
and get the results we wanted we would need to use brackets...
x = y + ((z - q) * (6 / t));
The two inner brackets are evaluated first, then the multiplication in the outer bracket is
evaluated, the result of these operations are then added to y and assigned to the variable x.
This is considerably different than what would result if the rules of precedence were applied
(indeed such a calculation would just about impossible to achieve without the brackets.
C++, like Delphi uses the ' dot operator ' to access fields of a record (on the stack) and a
pointer operators to access variables on the heap.
In Delphi, assuming we have a record 'info' with a field 'age' we could access this using the
dot operator if this was a static instance of the record (which would thus be on the stack, and
not require a pointer).
x := info.age;
If info was dynamically created using a pointer we could access the fields using a pointer
operator...
x := info^.age;
C++ also uses the dot operator for static records...
x = info.age;
To access a record on the heap (created with a pointer) the indirection operator in C++
looks like the following...
x = info->age;
Let us suppose that we have a pointer to an integer and wish to assign the value of the
integer to a variable. In Delphi the indirection operator would appear as follows...
x := intptr^;
In this case ' intptr ' is a pointer to an integer delcared as follows...
var intptr : ^integer;
In C++ the variable is declared as follows...
int* intptr;
And then the indirection operation looks like the following...
x = *intptr;
This is pretty much the same as in Delphi.
-
7/30/2019 Translating C++ Code Into Delphi
6/46
A short code segment illustrating how this works in Delphi...
procedure TForm1.Button1Click(Sender: TObject);
var px : ^integer;
x : integer;
begin
new(px);
px^ := 12;
x := px^;
showmessage(IntToStr(x));
end;
For a C++ example of the same sort of thing follow the link to the C++ section at the
bottom of the page and consult the section on C++ pointers...
C++ also has a bitwise not operator (the tilde ' ~ ')
x |= ~0x02;
What is meant is that this number will be combined with the variable x using a bitwise
NOT operation (each bit subjected to a NOT operation, in otherwords reversed from 0 to 1 or
from 1 to 0 before the OR operation is applied) and the result then assigned back to x. This
bitwise NOT operand is different than the logical NOT operand represented by the ' ! '
symbol, used in boolean expressions...
if (!something)
The scope resolution operator ( ' :: ') is discussed in the section on C++ Classes, and is the
operand with the highest precedence.
The table below illustrates operator precendence in C++ ... As you can see along with the
field operators (used in structures) brackets then have the next highest precedence, for the
reasons explained above...All operators in each group have equal precedence with assignment
operators of equal precedence being evaluated right to left and other operators left to right as
described above...(Therefore a bracket to the left of dot operator would have precedence,
which makes sense...)
:: scope resolution operator
. (the dot operator), -> (pointer), [ ] (square brackets), ( ) (round brackets), ++ and -- (post
increment/decrement), and the various types cast operands
~ (tilde - bitwise not), ! (logical not), + and - (unary), ++ and -- (pre increment/decrement),
, & (the address of operator), * (dereference a pointer), the new, the delete, and the size of
functions
.* (the dot operator and the dereference operator), ->. (pointer operator and the dot
operator)
-
7/30/2019 Translating C++ Code Into Delphi
7/46
* (as multiply), / , %
+, -
> (the assign out to and assign into operators, typically used in printing or file I/O)
, = (the relational comparison operators)
==, != (equivalence test operators)
& (bitwise and)
^ (bitwise xor)
| (bitwise or)
&& (logical and)
|| (logical or)
=, +=, -+, *=, /+, %=, =, &=, |=, ^= (assignment operators)
? : (? is equivalent to 'then' and colon is equivalent to 'else' operator, with the lowest
precedence so that everything is evaluated in the test statement before either the 'then' or 'else'
code is executed, which also makes sense).
-
7/30/2019 Translating C++ Code Into Delphi
8/46
Scope of Variables in C++Translating C++ Code into Delphi Pascal
In Delphi the scope of a variable is determined by where it is declared. For example a
variable declared within a function or procedure is 'in scope' (or known and visible) only
within that particular function or procedure. The variable is created when the function or
procedure is entered and executed and then once the function/procedure exits, the variable
goes 'out of scope' and is also destroyed and the memory it consumed is free. A variabledeclared within one procedure is not visible to any other procedure. A variable declared at the
head of unit is considered a global variable and is visible to every procedure and function
within that unit. Note that because of caching of memory in high speed memory areas on the
CPU it has been suggested that global variables be avoided as much as possible since they can
apparently cause a large number of cache miss penalties which slow down the performance of
system operating with such a cache. (The cache is normally loaded with memory which is in
the area of currently executing code, although this explanation is a bit of simplification,
however a global variable might exist in a far segment of memory not currently in the cache,
thus causing the cache miss).
In Delphi variables are declared before they are used at the beginning of the function or
procedure definition using the var statement, or within the declaration header of the procedure
or function if it is a variable that is passed to the function by another segment of code..
function TForm1.ThisFunction(i : integer):integer;
var x : integer;
s : string;
begin
the code is here
end;
In this example the variable 'i' is passed to the function and thus is declared in the header.
After the var statement the variables x and s are declared and then the body of the function
begins after the begin statement.
In C the situation is more complex. Variables can be declared within the body of functions.
This also introduces complications in regards to the scope of variables. For example we could
have two variables named 'x' declared within the body of one C function.
int x=10;
for (int x=20;x
-
7/30/2019 Translating C++ Code Into Delphi
9/46
imagine loops nested within loops within functions all of which declare variables named x,
each one with a different scope.
Because variables are in scope only within the code segment that declares them statements
such as the following will generate compiler errors.
int y;
for (int x=20;x
-
7/30/2019 Translating C++ Code Into Delphi
10/46
I f - then - else statements in C++Translating C++ Code into Delphi Pascal
The simplest form of an 'if - then' construct expressed in C is as follows.
If (x == 10)
DoSomething(x);
If an if - then type construct has more than one statement to execute C uses curly braces to
enclose the statements as follows...
If (x > 10)
{
DoFirstThing;
DoSecondThing;
}
C also allows the use of any non zero variable as the test in an if statement. As long as the
variable is non-zero the test evaluates true and the following statement executes.
If (x) DoSomething;
The exclamation point indicates the not statement in C. In the following example if 'not x'
the statement following will execute (in otherwords if x is zero, which makes the statement
false, the '!' (not) operator will change the test result to true, so that the following statement
executes)...
If (!x) DoSomething;
The else construct uses the following syntax...(Note the way the curly braces are used to
seperate the if construct from the else construct...)
If (x == 20)
{
DoSomething;
DoAnotherThing;
}
else
{
DoSomethingElse;DoSomeOtherThing;
}
If statements can be nested as follows...
If (x > 10)
If (x < 20)
DoSomething;
Nesting can also become more complex by including multiple statements, which then
requires the use of the curly braces to delimit the blocks of code...
-
7/30/2019 Translating C++ Code Into Delphi
11/46
if (x > 10)
{
DoSomethingOne;
If (x < 20)
{
DoSomeOtherthing;
If (x = 15)
{
DoThatThing;
DoThisThing;
}
}
}
else if (x > 0)
{
DoSomethingElse;
If (x = 1)
{ DoWhatEver;
DoWhatEverElse;
}
}
else {
DoThis;
DoThat;
}
In the above construct if x > 10 the first block of code is executed, otherwise the 'else if'
statement is tested and if x > 0 this second block of code executes. If neither of theseconditions is true (in this example x would less than 0) then the else block of code at the end
of the construct is the only code that will execute...
You can also see how keeping track of the curly braces can become complicated the more
complex the structure becomes. (When the code to be executed consists of only a single line
the curly braces are not required...) In the Borland version of C++ you can always find the
curly brace that matches any other curly brace by placing the cursor over the brace you want
to match and pressing ALT and the brace key (which, given that the shift key is not held down
would be either ' [ ' or ' ] ' ....
There is also an enigmatic looking 'short cut' syntax used in C for if - else constructs whichlooks as follows.
x == 10 ? y = 16 : y = 5;
The shortcut syntax means that if x is 10 then y becomes 16 else y becomes 5. Or, in
Delphi, to compare the two statements, the question mark is equivalent to 'then' and the colon
is equivalent to else...
If x = 10 then y := 16 else y := 5;
x == 10 ? y = 16 : y = 5;
-
7/30/2019 Translating C++ Code Into Delphi
12/46
You will also notice the different syntax for equivalency texts and for setting the value of
the variable. In Delphi a single equals sign ( = ) is the test for equivalency used in if
statements while double equals signs are used in C to test for equivalency ( == ). Delphi uses
a colon followed by an equal sign ( := ) to set a variable value, while C uses a single equals
sign to set variable values ( = )...
One difference between Delphi and C++ is the way the syntax for an if / then construct can
be written. For example the following line of Code would generate an error in Delphi (the
compiler will complain that it has the wrong operand type).
If x = 6 or y = 11 then
'6 or y = 11' is 'the wrong operand type' for a logical or operation. This should be written
as...
if (x = 6) or (y = 11) then
Both (x = 6) or (y = 11) evaluate to boolean (true or false) expressions and thus providevalid 'operand types' for the or operation. In C++ this could be written as follows...
if (x==6 || y==1)
The brackets are not required, since the equivalency test operator in both cases has a higher
precedence than the or operator.
-
7/30/2019 Translating C++ Code Into Delphi
13/46
The Switch statement in C++Translating C++ Code into Delphi Pascal
The switch statement simplifies complicated 'if then else' constructs in c ++ and is similar
to the case statement in Delphi. The variable to be tested comes after the switch statement,
and it can be variable, an expression or the result of a function.
switch(x){
case 1 :
{
dosomecode1;
break;
}
case 2 :
{
docodefor2;
break;
}
case 3 :
case 4 :
case 5 :
case 6 :
{
docodefor3456;
break;
}
default :
{dodefaultcode;
}
}
In the above example when x is 1 then code for 1 executes. The break statement is
optional, however if you do not include it the switch statement will go through all the other
comparisons, whereas with a break statement once a valid comparison is found the program
executes the code segment and does not bother checking the other comparisons which is more
efficient. When x is either 3, 4, 5, or 6 the same block of code will execute in each case, and
if x has none of these values then the final default code executes. It is not mandatory to
include a default switch, and it functions much like the final 'else' statement of a complicatedif - then - else construct.
-
7/30/2019 Translating C++ Code Into Delphi
14/46
Loops in C++Translating C++ Code into Delphi Pascal
The syntax for a repeating for loop in C++ follows this pattern
for (StartingPoint; ConditionalExpression; Increment)
{
DoStatementOne;DoStatementTwo;
}
If there is only a single statement to be repeatedly executed within the loop the curly braces
are not required.
The loop will execute as long as the conditional expression is non-zero. The loop begins at
Starting point, and each run through the loop the starting value is changed by the amount
specified by increment.
An example of what this looks like in practice
for (int x=1;x0;x--)
{
the code is here;
}
In this case the loop begins at 20, and each iteration the value of x is reduced by one andthe loop terminates at 0 (continues as long as x is greater than 0).
Variables can also be used int he conditional section of the loop declaration and the loop
can be incremented or decremented by an amount other than 1 using the following syntax...
for (int x=10;x>y;x+=4)
{
code hre;
}
-
7/30/2019 Translating C++ Code Into Delphi
15/46
In this example the x begins at 10 and each iteration x is increased by adding four and the
loop terminates when x becomes greater than y. Similarly the expression x-=4 would
decrease x by four each time throught the loop.
The while loop executes as long as the conditional expression remains true (therefore it is
required that the code within the loop manipulates the conditional expression in such a way as
to eventually result in the stopping condition so that the loop does not execute forever...)
int x;
while (x < 10)
{
DoSomething;
x = SomeCodeResult;
}
Once again the syntax for declaring a variable is different than in Delphi in this example as
x is declared as an integer just before being used in the loop. The loop code manipulates the
variable x and should eventually return a result that makes x greater than or equal to ten sothat the loop terminates at some point.
A variation of the code manipulates the variable within the loop declaration itself.
int x = 10;
while (x++ < 20)
{
Dosome code here;
}
Here x is set to the value 10 and then incremented by one each time through the loop withthe loop terminating when x reaches the value 20.
C also has a 'do - while' loop structure that mimics the 'repeat - until' structure in Delphi.
int x = 10;
do
{
code is here;
x = something;
} while (x < 20);
This is similar to the following Delphi code (with the difference that in Delphi the variable
x would be declared as integer at the beginning of the function or procedure)...
x := 10;
repeat
do some code;
x := something;
until (x >= 20);
The C language also has two special statements that resemble statements used in assembly
language, the continue and the break statements. The continue statement causes the programto skip whatever code follows in the loop and go on to the next iteration of the loop. The
-
7/30/2019 Translating C++ Code Into Delphi
16/46
break statement jumps out of the loop altogether and goes onto the code following the loop
(the code which would normally be executed once the loop terminated).
The following simple example assumes that when the loop value is 5 the code following
should not be executed and the loop should simply move on to the value 6.
for (int x=1;x
-
7/30/2019 Translating C++ Code Into Delphi
17/46
Structures in C++ and records in DelphiTranslating C++ Code into Delphi Pascal
A structure in C is analogous to a record in Delphi. A record can be thought of as a kind of
complex variable declaration.
type
MyData = recordphonenum : string;
married : boolean;
age : integer;
name : string;
end;
var
Datalist : MyData;
In this example we use the type statement to create a custom data type, in this case a record.
The declaration of the record can then be used in the var statement to initalize an actual
instance of the declared data type. The record above is a kind of a custom variable which
consists of a number of different variable types stored as a single data record.
We can access a field of the record using the dot operator.
thephonenum := Datalist.phonenum;
Sometimes it is more convenient to use pointers to records.
type MyDataListPointer = ^MyData;
The declaration of the MyData record then follows as above and then in the var declaration
we declare a pointer to a record instead of a static instance of a record.
var
DataListPtr : MyDataListPointer;
We then create an actual record from this pointer using the new statement.
new(DataListPtr);
And we access the fields of the record using pointer syntax.
DataListPtr^.phonenum := '336-3344';
There are many variations in the way records (or structures) are stored and manipulated
(linked lists, arrays of records, etc.) It is impossible to access common graphics formats,
Windows or Linux API functions, etc., without dealing with records and so this is a very
common and very important data type. Records, or structures as they are called in C, are used
throughout APIs and as interfaces to many standard functions. (For example a common
bitmap graphic consists of a structure (or record in Delphi), which is referred to as the
Bitmap info header, which stores in record form all the information about the height, width,
color depth, etc., of the following raw bitmap data in the file.) The usefulness of records (or
-
7/30/2019 Translating C++ Code Into Delphi
18/46
structures as C calls them) in database applications should be obvious, and they are used in
any application where related data must be stored conveniently in a single structure.
If you are familiar with records in Delphi, the structure in C encapsulates the same idea
using the syntax defined for this purpose in the C language. The keyword 'record' and the
syntax illustrated above is used to define records in Delphi, and C uses the keyword struct and
the following syntax...
struct MyData {
char phonenum[8];
bool married;
int age;
char name[20];
};
This struct statement creates the equivalent of the record declared in Delphi above. This
declaration defines the 'type' of the structure, just as using the type declaration in Delphi to
declare a variable of the type record. To create an instance of the record in Delphi you use itin the var declaration as the variable type for the variable being declared as demonstrated
above. In C you can crate an instance of the structure by including the name of the variable
after the closing curly braces and before the final semi-colon which completes the definition.
struct MyData {
char phonenum[8];
bool married;
int age;
char name[20];
}Datalist;
In the example above we have described the type of the structure and then we created an
instance of the structure - a variable given the name Datalist. Note that C is case sensitive, so
a variable named Datalist is not the same as a variable named datalist or DataList, whereas all
three would refer to the same variable in Delphi, which is not case sensitive.
Alternatively, an instance of the structure can be declared at any place in code prior to
using the structure, just as any other variable is declared in C. Unlike in Delphi, the variable
type precedes the name of the instance of the variable.
MyData DataList;
This declares a variable DataList which is the type MyData, which means that the program
allocates memory for the structure defined above, creating an instance of this record.
The fields of the record can then be accessed using the dot operator, as in Delphi, and using
standard C functions.
To copy a string into a field within the structure...
strcpy(Datalist.phonenum, "383-4455");
or to fill in other fields...
-
7/30/2019 Translating C++ Code Into Delphi
19/46
Datalist.age = 51;
Datalist.married = true;
And so on...
Instances tructures can be declared as arrays of records, for example as part of a database
application. Here we will declare an array of 20 records...
MyData Datalist[20];
Each individual record can then be accessed using array syntax combined with the dot
operator used to access record fields...
Datalist[0].age = 51;
And so on...
Structures can also be filled in using the following syntax. Let's assume that the a record
exists with a string field, and a couple of integer fields, and the structure name is Datalist.
Datalist = { "Some Name", 12, 102};
Arrays can also be filled in the same way. The following C++ example creates an array of
3 strings.
string sarray[3];
sarray[0] = 'Hello';
sarray[1] = 'there';sarray[2] = 'world';
This could also be accomplished using the following code...
string sarray = {'Hello', 'there', 'world'};
Initializer lists (like the above) can be used when the array is declared and in this case the
dimension of the array can be omitted (the compiler can make the determination based on the
initializer list).
Arrays can also be dynamically created, and the dimension can be passed as an integervariable, making the declaration truly dynamic. Here we assume that x is an integer variable
that has been previously initialized to the required number of strings in the array. Notice that
declaration is different in that the use of the pointer symbol (the asterik) indicates that this is
going to be a dynamic array...
string* sarray = new string[x];
sarray[1] = 'Hello';
sarray[2] = 'there';
sarray[3] = 'world';
-
7/30/2019 Translating C++ Code Into Delphi
20/46
Here the programmer is going to have to code to make sure that bounds of the array are not
violated, since the compiler will not know if '3' is out of range since the array was declared
dynamically using a variable (x in the example above).
In C++ the name of an array is actually a pointer to the first element in an array. When
arrays are passed to functions as parameters you might see either of the two following ways of
describing the array...
int somefunction(int anarray[], int size);
int somefunction(int* anarray, int size);
Both the above declarations describe an array parameter passed to a function, with the
pointer pointing to the first element in the array.
Within the body of the function the elements of the array are then accessed using the usual
syntax...
anarray[2] = 12;
Multidimensional arrays are declared as follows...
int anarray[4][6][7]; ... a 3-d array...
Initializer lists can be used when the array is declared...
int anarray[2][2] = { {2, 4}, {5, 6} };
C++, like Delphi, allows types to be declared and then the type to be used to declareinstances of variables. In the example above the delphi 'type' keyword was used to declare
records consisting of variables. This record type was then used to create an actual instance of
the record type.
In C++ the keyword used is 'typedef' (the definition of a type which can then be used to
create instances of variables...) In both Delphi and C++ arrays can also be declared as types
and then the type used to create actual arrays (typically this is done in the case of
multidimensional arrays). The type definition is included at the top of the unit...
#include
...etc etc ...const int X = 12;
const int Y = 14;
typedef int TwoDArray[X][Y];
..etc etc ...
int somefunction(TwoDArray, int, double);
In the body of the code the type (TwoDArray) is then used to create instances of the
variable in the usual way...
TwoDArray anarray;
-
7/30/2019 Translating C++ Code Into Delphi
21/46
An initializer list can also be used to put values into the array as it is delcared as illustrated
above, although it is also typical to use loops (for loops in particular) to initialize or otherwise
manipulate arrays...
The fields of a multidimensional array are accessed as follows...
anarray[2][8] = 32;
-
7/30/2019 Translating C++ Code Into Delphi
22/46
Pointers in C++, the stack and the heapTranslating C++ Code into Delphi Pascal
When variables are created in a C++ program (when the variables are in scope) the memory
required to hold the variable is allocated from the program stack, and when the variable goes
out of scope, the memory which was taken on the stack is freed. When memory is allocated
dynamically (by the programmer) memory is taken from the heap (which consists of all of the
computers virutal memory, which includes onboard RAM and space available on the diskdrive. as in Delphi, with C++ memory is allocated dynamically using the new operator.
As an example of static (stack) variables, and dynamic variables (on the heap) consider a
simple integer x. The static variable would be declared in Delphi and C++ as follows...
Delphi...
var x : integer;
In C++ variables do no need to be declared in the 'var' segment at the beginning of a
function (or unit) but can be declared anywhere in code. (If a variable is declared within a for
loop, then the variable would only be in scope (on the stack) while the for loop executed, and
then would vanish and go out of scope (off the stack) when the for loop exits. This idea of the
scope of a variable being limited to the section of code where it was declared is an important
difference between C++ and Delphi...
The same integer declared somewhere in C++...
int x;
Let's assume that the address of x was 1000. X is an integer, which currently means it is a
32 bit value so memory is allocated on the stack for four bytes1000, 1001, 1002, and 1003.
The address of x would be 1000 (so when we use the variable x we are actually referring to
the address 1000). When we assign a value to x we are assigning this value to the four bytes
beginning at the address to which x acts as a pointer (the variable name).
We can also create variables dynamically in which case they do not go onto the stack but
rather onto the heap. Because the program stack is limited, and the heap consists of all virtual
memory (RAM and disk space) pointers and heap variables are useful in particular for large
records (structures in C++) but can also be used for smaller variables.
To begin with I will create a simple variable on the heap, dynamically, using pointers...The
variable ' px ' will be a pointer to an integer...
In Delphi....
var px : ^integer;
begin
new(px); {allocates 4 bytes for the integer);
....use variable etc.....
dispose(px); {frees the 4 bytes);
px := nil; {the pointer was 'dangling' - set it to the null value}
-
7/30/2019 Translating C++ Code Into Delphi
23/46
To access the value pointed to by ' px ' we deference the pointer. Assume that x is also an
integer and we wish to assign it the value pointed to by ' px ' (or vice versa).
x := px^;
px^ := x;
In this simple example the address of x is 1000, the address of 'px' (the pointer) could be
2000. The value stored in px is 1000 which is the address of x since px points to x. We could
also assign the pointer the address of an integer using the 'address of' operator...Assume that
we have an integer variable y...
px := @y;
The pointer px is now pointing to the address of y. If we dereference px we get the integer
value stored in y. If we check the value of px itself (we do not dereference the pointer) we get
the address of y in this case ...and not the integer value stored in y.
In C++ the same sort of things would be accomplished as follows...
int* px = &x; ...the pointer px points to x using the address of x operator...
y = *px; ...the pointer is deferenced and now y = the integer value stored in x....
int w = *px; ...w now is set equal to the integer value stored in x...
The dereference operator (' * ') and the address of operator (' & ') perform two functions,
depending on the context in which they are used.
int* px;
Px is a derived type. It is a pointer to an integer, so its type 'pointer to an integer' is derived
from the type integer. It is also a dangling pointer (it points to nothing at the moment). We
could set it to null in two ways...
px = 0; ...a null pointer...
int* px = 0; ...set to null when it was declared...
In a different context the symbol becomes the dereference operator...
y = *px; ...assume px points to x, this dereferences the pointer so that y now is equal to x...
The ' * ' operator can also mean 'multiply' depending on the context.
y = x * 2;
Thus these operators can have multiple meanings in a C++ program dependant on context
and this is something to keep in mind...
Pointers are frequently used for large data structures. For example let's assume that a
structure (record) exists which has been defined with the label 'arecord'. To dynamicallycreate an instance of a variable of this type the new statement is used in the following way...
-
7/30/2019 Translating C++ Code Into Delphi
24/46
First we will declare a pointer to a record (which will then be used to dynamically allocate
memory from the heap for the record.) The pointer will be given the label MyRecord and the
asterik indicates that we are defining a pointer to a structure of the type 'arecord'
arecord* MyRecord;
MyRecord = new arecord;
You will notice that the variable declaration in C++ is 'backwards' compared to what you
are used to in Delphi. An equivalent definition in Delphi which mimics the above could be
implemented as follows...
type
arecordPtr = ^arecord; {a variable arecordPtr which is a pointer to arecord}
arecord = record {define the structure of the record}
{some records fields are here}
aqe : integer;cost : float; {etc etc}
end;
{....}
var
MyRecord : arecordPtr; {MyRecord is a variable of the type which points to arecord}
{...}
begin
New(MyRecord);
{create an actual instance of arecord structure - this 'new' function does not create the
pointer, but rather the record}
Later we can free the memory occupied by the record using the Dispose statement (which
disposes of the record, not the pointer) followed by setting the now invalid pointer to nil...
Dispose(MyRecord);
MyRecord := nil;
Using the new statement in either Delphi or C++ dynamically allocates memory from the
heap (leaving room on the stack for local variables) and the function returns a pointer to the
memory which has been allocated. Note that pointers themselves are allocated from the stack,
just like local variables (a 32 bit pointer would use 4 bytes from the stack, and then the
memory required for the record would be allocated from the heap).
Let's assume that the type 'arecord' has two integer fields, one named 'amount' and another
'paid'
To access the record fields using a pointer in Delphi one uses the following syntax...
MyRecord^.amount := 10;
MyRecord^.paid := 5;
In C++ the syntax for accessing the field of a structure pointed to by a pointer is as
follows...
-
7/30/2019 Translating C++ Code Into Delphi
25/46
MyRecord->amount = 10;
MyRecord->paid = 5;
This can be compared to accessing the same fields from a record declared as a static
variable using the dot operator (which would take up memory on the stack, rather than the
heap).
arecord MyRecord;
MyRecord.amount = 10;
In Delphi the dot operator works exactly the same...Here once again the record is a static
variable stored on the stack, thus not requiring a pointer...
var MyRecord : arecord;
begin
MyRecord.amount := 10;
As an alterrnative to a linked list the records could be stored as an array of pointers (ratherthan as an array of the records themselves...
In Delphi we could declare the array as follows...
MyRecord : array[0..10} of arecordPtr;
The records themselves would have to be created using the new operator (since all we have
created so far is an array of pointers).
new(MyRecord[0]);
The elements in each record could then accessed using a combination of array and pointer
syntax as follows ...
MyRecord[5]^.amount := 10;
In C++ this would appear as follows...
arecord* MyRecord[10];
Once again each record pointed to by one of these array pointers would have to be
individually created by using the new function.
MyRecord[4] = new arecord;
And the individual fields in a given record would once again be accessed in C++ using a
combination of the array and pointer deferencing syntax...
MyRecord[5] -> amount = 10;
Keep in mind here that C++ is case sensitive. In Delphi MyRecord and Myrecord or
myrecord are all the same variable (Delphi is not case sensitive) while in all three cases in C+
+ these would be different variables. As well in C++ pointers are not initialized to 'nil' butpoint at random places causing all those familiar pointer errors when running computer
-
7/30/2019 Translating C++ Code Into Delphi
26/46
programs (the most common being 'This program has performed an illegal operation and will
be shut down'). Pointers can be set to 'nil' in Delphi, or set to '0' in C++ to help avoid these
problems (program code would have to ensure that the pointer is never used if it has the null
value). In the following C++ example MyRecord is to declared to be a pointer to the structure
of type 'arecord' and then is set to '0'...
arecord* MyRecord = 0;
The record pointer could also be declared to be pointing at a record...
arecord* MyRecord = new arecord;
A few other bits and pieces of pointer syntax in C++ as compared to Delphi...
Let's suppose that a pointer is pointing to an integer variable, and we wish to assign the
value of this integer to another integer. In the example below we have an integer called
'amount' pointed to by a pointer 'amountPtr' and we wish to assign this integer value to
another variable labelled 'storeAmount'.
First we will declare a pointer to an integer and then assign as the value of the pointer the
address of the integer variable amount (note that this is the address of the variable amount,
and not the value contained by the variable, two different things...a pointer requires the
address of the variable which is being pointed to...)
int* amountPtr = &amount;
In Delphi
var amountPtr : ^integer;begin
amountPtr := @amount;
To assign the value of amount to StoreAmount ...
StoreAmount := amount; ... or in C++ ... StoreAmount = amount;
Dereferencing a pointer to do the same thing...
In Delphi..
StoreAmount := amountPtr^; {which means assign the value pointed to by amountPtr}
In C++...
StoreAmount = *amountPtr;
In both cases, because amountPtr points to amount, StoreAmount is assigned the value of
amount...
You might notice the reference operator in a C++ program as well. A reference is simply
an alias for an already existing variable. References must be initialized when they are
-
7/30/2019 Translating C++ Code Into Delphi
27/46
declared and are treated like constants (you cannot assign a value to the reference after it has
been declared...)
A reference can become an alias for any other variable. For example, rx will become the
alias for x in this C++ example...
int x = 12;
int& rx = x;
Add 4 to the reference variable rx and x also gets becomes 16. From this point on 'rx' and
'x' refer to the same variable (rx is a reference to x).
Let's assume that a function is declared which uses the 'var' keyword in Delphi.
function TForm1.somefunction(var x : integer):integer;
Now the use of the keyword 'var' tells the compiler to pass the integer to the function 'by
reference'. In otherwords if the function changes the value of 'x' it will also be changing theoriginal integer that was passed to the function. If the 'var' keyword is not used then the
parameter is passed by value (no reference, or alias is created for the original variable). Any
changes to the integer we named 'x' will not alter the original variable that was passed to the
function.
In order to achieve the same effect in C++, we pass variables to a function by reference, so
the function just declared above would like the following...
int somefunction(int&); ...the function declaration at the top of the unit...
int somefunction(int& x) ...the function header in the body of the unit...
{ ...the code }
Because x is declared to be 'passed by reference' (note the & operator) this has the same
effect as using the var keyword in Delphi. An alias is created for whatever integer you pass
the function. Any changes made to 'x' in the function will not be local but will also be
changed in the original variable you passed the function. For example let's assume that a
function takes an integer which we will call 'huey' and a calling routine passes the integer
'dewey' to the function. While the function is in scope, huey is now a reference to dewey, and
changes to huey are changes to dewey. If however the function was declared without the var
keyword (Delphi) and without the reference operator (in C++) then dewey will be passed byvalue. Only the value contained by the variable dewey will be passed to function but a
reference to the address of the original variable (dewey) will not be passed. Changes to huey
will not affect dewey since all huey recieved was the value contained by dewey and not a
reference to dewey. This is the difference between passing by reference and passing by value,
and it also illustrates one of the more common uses of the reference operator in C++..
Another thing to keep in mind is that when a variable in either language is passed by value
this creates a copy of the variable for the reasons noted above, while if the variable is passed
by reference no copy is made since the original variable will be used (and changed or altered).
So then for really large structures a programmer could either create a global variable or the
structure could be passed to functions by reference to avoid the overhead of having a functioncreate a copy of the large structure, as would happen if it was passed by value. In addition,
-
7/30/2019 Translating C++ Code Into Delphi
28/46
any changes made to this copy would vanish when the function completed and the copied
variable went out of scope, which is probably not what you wanted (the original structure
would be the same after the function executed, which would mean nothing was really
accomplished).
If you wanted to be really safe, however, and do not want the function to alter a structure or
large graphic you could pass the variable by reference but declare it to be a constant. This
avoids the copying of a large structure, but also protects the original variable from unwanted
alterations...
int somefunction(const x&);
The variable x in the above example is both a constant and passed by reference.
In programming code it is probably more typical to see references used as an alias for a
pointer, and the end result is that coding is simplified, and thus looks like slightly different in
the program code. This syntax describes a pointer which is accessed, like an object or stack
declared record, using the dot operator rather the pointer syntax { -> }. There may be otherdifferences internally, but externally this is about all the visible difference that exists between
a 'reference' and a 'pointer'...A reference is a pointer declared with the ampersand rather than
with the asterik, which points to another pointer to the structure. This sounds confusing but
looks something like this....
arecord& MyRefRecord = *new arecord;
MyRefRecord is then declared as a reference to a pointer to arecord structure. The fields of
this record are then accessed using the dot operator.
MyRefRecord.amount = 10;
This can be compared to accessing the field of MyRecord assuming it was a pointer to the
record...
MyRecord->amount = 10;
A reference might be declared in slightly different ways in a computer program. For
example let us assume that we already a variable MyRecord which was a pointer to a record,
as just suggested above. We could access MyRecord fields using the -> operator or we could
go a step further and create a reference to this pointer, the difference being that we will then
access the fields of the record using the dot operator. To do this we do another step anddeclare a reference to the MyRecord pointer as follows...
arecord& MyRefRecord = *MyRecord;
Note that references must be assigned a value when they are declared (this is not true of
pointers which can be assigned a value later, or have their value changed, and this seems to
another functional difference between references and pointers...) MyRecord is a pointer and
MyRefRecord is a reference which points to the pointer MyRecord, and then the fields of
MyRefRecord will access this record using the dot operator as illustrated above. So then if
the fields of a structure are accessed in a C++ program using the dot operator, it could be a
simple record (a type of local variable stored on the program stack) or it could be a referenceto a pointer to a record stored in the computer's memory heap (in this case the program listing
-
7/30/2019 Translating C++ Code Into Delphi
29/46
will indicate this fact by the presence of the apersand ( & ) in the variable declaration.) You
might notice that the ampersand is also the 'address of' function used on variables, but because
the context is different, in one case it creates a referenced pointer and in the other it returns
the address of a variable (when it is after an equals sign).
Referenced pointer...
arecord& MyRefRecord = *MyRecord;
Address of operator (we will assume that IntPtr is a pointer to an int, and amount is an int)..
IntPtr = &amount;
This places the address of the amount variable into the pointer IntPtr, as described above...
As I mentioned above, memory is dynamically allocated in C++ using the new operator.
When a programmer allocates memory it is the responsibility of the programmer to free this
memory (to avoid those memory leaks). Note that C++ is more simplified in this respect thansimple C which uses malloc (assigns memory), calloc, realloc, and then free to manipulate
program memory
One of the advantages of using 'new' and 'delete' (or 'new' and 'dispose' in Delphi) is that
program stack memory is limited, but the heap in comparison is very large. For example
pushing a large array onto the stack might not be a good idea, as your program will crash or
perform unpredictably if stack memory is exhausted, and in the case of very large arrays or
strings using a pointer and thus allocating and deallocating heap memory is a better choice,
and the only difference is in becoming comfortable with working with pointers (and thus the
heap) rather than using static variable definitions (and thus the stack). The memory used by
local variables (variables declared in the static manner) is automatically managed by theprogram, with variables being disposed of when they go out of scope. However memory
management becomes the programmers responsibility when using pointers to the heap and the
new and delete (or dispose) functions.
For example, here the memory for the record is allocated, and then freed...
arecord* MyRecord = new arecord;
delete MyRecord;
MyRecord = 0;
If you need to delete a memory that was accessed using a referenced pointer the syntax
looks like the following...The address operator ( & ) extracts the address of the pointer from
the reference and then this pointer is used to delete the actual allocated memory...
delete &MyRefRecord;
In C++ it is also a very bad idea to delete an uninitialized pointer (that was never set to
point to something) since this can cause problems in memory wherever the random pointer
may be pointing to at delete time. It is also a bad idea to delete a pointer multiple times in
different parts of the program. Setting the pointer to null allows for testing of these conditions
-
7/30/2019 Translating C++ Code Into Delphi
30/46
For this reason a pointer in C++ should be set to point to something (trying to dispose of a
pointer in Delphi that points to nothing generates an error).
arecord* MyRecord = 0;
delete MyRecord;
In the above example the pointer is set to Null (MyRecord := nil; in Delphi). I have a habit
of setting all pointers to the null value when memory is disposed of to avoid crashing
programs (this requires testing for a null value before using a pointer and taking appropriate
steps if for some reason a null pointer is being accessed, since accessing a null pointer is
enough in itself to generate an 'illegal operation' and a program error).
How it looks in Delphi...
dispose(MyRecord);
MyRecord := nil;
Dynamic arrays can also be created and disposed of using new and delete (thus using heap
rather than the stack).
Using Delphi 5 (and perhaps 4) creating dynamic arrays has been simplified. First the
array declaration indicates that the array is gong to be dynamically allocated.
var
SomeArray : array of integer;
Later in program code the array will be dynamically allocated using the 'SetLength'
function. For example, suppose we decided that we needed 100 elements in the array...
SetLength(SomeArray, 100 * sizeof(integer));
The situation is a little more complicated in earlier versions of Delphi...see the page on
Dynamic arrays for more information on creating and disposing of dynamic arrays in Delphi...
In C++ a dynamic array is created using a pointer and then disposed of using delete[] ...
Note that the two square brackets are required when deleting the memory allocated to a
dynamic array.
char* SomeBuffer = new char[1000]'
delete[] SomeBuffer;
-
7/30/2019 Translating C++ Code Into Delphi
31/46
Passing variables by reference of value in C++Translating C++ Code into Delphi Pascal
In Delphi it is possible to pass a local copy of a variable when calling a procedure or a
function, and it is also possible to pass the actual variable itself.
For example the following code passes a local copy of the integer variable 'amount' to a
procedure...]
procedure TForm1.PassAnInt;
var amount : integer;
begin
amount := 10;
ChangeInt(amount);
showmessage(IntToStr(amount));
end;
procedure TForm1.ChangeInt(amount : integer);
begin
amount := 22;
end;
When showmessage executes 'amount' is shown to be equal to 10 (the variable 'amount' in
the second procedure was working on a second copy of a variable also named 'amount' which
was in scope only while the ChangeInt function was executing. Because amount was passed
to the procedure by value the original value of amount remains unchanged. However there
are other ways to pass variables which would actually change the orginal variable 'amount'.
For example by using the 'var' key word in the procedure definition this indicates to the
compiler that the variable is to be passed 'by reference' (rather than 'by value').
procedure TForm1.ChangeInt(var amount : integer);
In this case when the showmessage procedure executes 'amount' now causes the display of
the value of 22. The actual variable was changed and no local copy was made, because a
reference to the actual variable being passed was sent to the procedure as a pointer to its
location in memory. Note that this would work no matter what name was given to the
variable in the procedure or function declaration...
procedure TForm1.ChangeInt(var anyname : integer);
In this example the anyname would be set to 22, and once again 'amount' would be
displayed as having the value 22, since the var statement in the header indicates that a pointer
to the original variable is to be created, so that the procedure or function actually changes the
original value. This is a feature often used in API functions as a way of returning more than
one result from a function (while the function returns a result in the usual way, the inclusion
of pointers in the header declaration results in multiple results being returned in these
variables as well).
It also makes sense then that another way to alter an original variable or a set of variables is
to pass actual pointers to these variables to a function or procedure, and in this case, since the
pointer is pointing to the actual memory locations of these variables any changes made to
-
7/30/2019 Translating C++ Code Into Delphi
32/46
them in another function is going to change the original values. Passing a pointer to a
variable is thus the same as using the 'var' keyword in the function header as illustrated above.
procedure TForm1.PassAPointer;
var
amount : integer;
intPtr : ^integer;
begin
amount := 10;
intPtr^ := amount;
ChangeInt(intPtr);
showmessage(IntToStr(amount));
end;
procedure TForm1.ChangeInt(apointer : ^integer);
begin
apointer^ := 22;
end;
In the above example the procedure ChangeInt acts like a function by 'returning a value'
(because it was passed a pointer to the variable amount it actually alters the value of amount
when the object pointed to by the pointer is set equal to 22).
Passing by reference (using either a pointer or the var declaration in the header) makes
sense if you want to return multiple values from a procedure or function, or when the objects
being passed are quite large, such as strings, graphics, or objects (a local copy of these large
data structures need not be made which is quicker and also a more efficient use of memory).
There is syntax in C++ that resembles the 'var' statement in Delphi and produces the sameresult (rather than creating a local copy of a variable being passed the original is used). The
ampersand is used in the function or procedure declaration to indicate that the address of the
variable is being passed rather the value contained by the variable - in otherwords a reference
to the variable is being passed, the equivalent to passing a pointer, which, after all, just
contains the address of the variable. In C++ this would look like this...
void ChangeInt(int& amount)
{
amount = 22;
}
The keyword 'void' indicates that this routine returns no value (in otherwords it is a
procedure, since functions return a value). The ampersand indicates that the variable 'amount'
refers to a variable being passed by reference, so as in the Delphi example above, this variable
will actually be set to the value 22.
Records (or structures as they are called in C++) can also be passed by reference and in the
API this method would be the typical case, since often structures are quite large.
void DoChanges(arecord& MyRecord)
{
-
7/30/2019 Translating C++ Code Into Delphi
33/46
...all the program code here will alter the fields in 'MyRecord', which was passed by
reference and so refers to the actual address of the original record...
}
-
7/30/2019 Translating C++ Code Into Delphi
34/46
File I/O in C++Translating C++ Code into Delphi Pascal
C++ handles basic file I/O using streaming classes, from which instances of a streaming
variable are created. There is a similar feature in Delphi - TFileStream.
For example we could declare a file streaming variable in Delphi as follows...
var
FileStream : TFileStream;
begin
FileStream.Create('c:\something.txt', fmOpenWrite or fmCreate);
FileStream.write(buffer, count);
FileStream.free;
end;
In C++ the same thing would be accomplished using the classes ofstream, ifstream, and
fstream.
Ofstream handles output, ifstream handles input and fstream handles input and output.
FStream seems similar then to the file variable in Delphi.
var
AFile : file;
amt : integer;
begin
Assignfile(AFile, 'c:\somefile.brg');
Reset(Afile, 1);
Blockwrite(Afile, buffer[1], 200, amt);....
Blockread(Afile, buffer[300], 12, amt);
....
end;
In the above example the untyped file 'AFile' is opened using 'Reset' and is set to one,
which means that the 'record size' for writes is one byte. The Blockwrite statement begins
writing at 'Buffer[1]' , writes out 200 bytes, and then returns the number of bytes written in
'amt'. Later, aftering 'seeking' a spot in the file more than likely, the same file variable is used
to read into the buffer. Reset allows both read and write, similar to the fstream variable in C+
+. TFileStream also allows either read or write or 'fmOpenReadWrite' which also mimicsfstream in C++.
The following is an example of using the ifstream variable in C++ to write out a file.
remember to include the line #include at the head of the source file...
Let's assume that we have created a character buffer to store the input...
char buffer[1024];
We need to create an instance of the ifstream class...
-
7/30/2019 Translating C++ Code Into Delphi
35/46
ifstream AFile("c:\somefile.txt");
A line can be read in as follows ...
Afile.getline(buffer, sizeof(buffer));
The destructor for the class closes the file if it is still open or you can explicitly close the
file yourself...
Afile.close();
The code above passes a PChar (pointer to a character array) to the constructor of the
ifstream class. It is also possible to use the following line to open a file...
ifstream.Afile;
AFile.open("c:\somefile.txt");
One thing to watch out for if you are a Delphi programmer is that C++ uses double quotesfor literal strings while Delphi uses single quotes.
To write to a file you can create an instance of the ofstream class, and then use the '
-
7/30/2019 Translating C++ Code Into Delphi
36/46
An example of reading a file in and then writing it out using a string variable in C++...
#include
int main()
{
ifstream readfile("c:\readfile.txt");
ofstream writefile("c:\writefile.txt");
string thetext;
while (getline(readfile, thetext))
{
writefile
-
7/30/2019 Translating C++ Code Into Delphi
37/46
Assignfile(afile, 'c:\something.txt');
Rewrite(afile, 1); {set record size to one byte}
Blockwrite(afile, buffer[0], 80, amt);
In this example the record size (in the rewrite procedure) was set to one byte. If we set the
record size to 80 then we could write Blockwrite(afile, buffer[0], 1, amt); and write out 80
bytes this way as well.
Let's assume that a record (or structure in C++) exists which has a name field followed by
two integers, and we will refer to it as theRecord. The structure can be written to disk as
follows...
ofstream afile("c:\somefile.txt", ios::binary);
afile.write((char*)&theRecord, sizeof(theRecord));
The code (char*) is neccesary to 'cast' the record to a PChar which is required by the
fstream write function. If you are familiar with using the Windows API in Delphi you know
that Delphi strings must be cast to pointers to character arrays as well (Pchar(TheString) inDelphi code).
The code &theRecord extracts the address of the record and so the bytes in the record
starting from this address are cast to a PChar and then this PChar is passed to the write routine
and written out to afile. This accomplishes basically the same thing as the Delphi code above
and this could also be done using the TFileStream class in Delphi, passing the number of
bytes in the record structure as the argument to the writebuffer method of the streaming class.
(For some reason I am not clear on, the writebuffer method works while the write method
does not, but this could have something to do with the way the internal addresses are used in
these two methods). As well, note that if you are reading in a record from disk you must use
the same code as above, casting the record structure to a PChar, since the read function of theifstream class also requires a character pointer as its operand.
When a file is open for read or for read/write it is possible to do random access on the file
using the seeking functions provided by both Delphi and C++. (Note that in Delphi you
cannot seek using a textfile but rather you must use a typed or, as in the examples above, an
untyped file variable). Assume that afile is an untyped file variable...
var afile : file; fpos : longint;
begin
Assignfile(afile, 'C:\something.txt');
Reset(afile, 1);fpos := sizeof(theRecord) * 122;
Seek(afile, fpos);
The Tfilestream class also has a seek method...
var filestream : TFilestream; offset : longint; origin : word;
begin
filestream := TFilestream.create('c:\something.txt', fmopenread);
filestream.seek(offset, origin);
Typed and Untyped files can return their file postion using FilePos which returns alongint...
-
7/30/2019 Translating C++ Code Into Delphi
38/46
lx := FilePos(afile);
As does TfileStream...
lx := filestream.position;
In C++ the fstream class has a seek function and a file position function, for either the
ifstream or the ofstream classes. Seekg() and tellg() seek or return the position for ifstream
variables while seekp() and tellp() do the same for ofstream classes. The usage of the
functions looks like the following...
ifstream afile("c:\something.txt", ios::binary);
int pos;
afile.seekg(0, pos)
pos = afile.tellg();
By opening a file in append mode tellg() would return the size of the file in bytes. Byopening an ofstream variable in read/write mode parts of a file could be accessed and changed
randomly.
-
7/30/2019 Translating C++ Code Into Delphi
39/46
C++ header interface filesTranslating C++ Code into Delphi Pascal
Just as a Delphi program can consist of individual units, a C++ project can consist of any
number of individual source files. Delphi uses the keyword 'uses' to indicate which source
files are to be referenced by any given unit (making the code in that unit visible to the calling
unit) and a C source file uses the keyword ' #include ' to perform the same function. Each
source file in a C++ program ends with the extension ' .cpp ' and the compiler creates fileswith the extension ' .obj ' from these source files. The task of the linker is then to take these
object files and link them together into an executable file ending with the familiar extension
' .exe '.
A C project also includes a header file (ending with the extension ' .h ' in the typical case
(although apparently you might also see ' .hpp ' indicating a C++ header file in particular. The
header file typically contains declarations of all the classes and all the structures ( or 'records')
in the program. These declared structures can then be accessed by a C++ source file using
the #include keyword (which you see at the top of C++ files). For example ' #include
something.h ' would include all the structures and classes declared in this header file, and also
any functional declarations provided in the header (note that the functional code itself is not
included in a header file, rather the header points to such code which will be found in another
file). The use of header files for classes and structures promotes code reusability and
modularity, since by the use of the include statement the records can be easily incorporated
into any new program which requires them. The information in the header file is required by
the programmer to access classes (not to mention DLLs) and also by the compiler which
needs to know how to incorporate these classes into the program. The header file forms the
interface between programmers code and the classes or the DLL.
A simple header example :
#indef _ARECORD_H
#define _ARECORD.H
struct somerecord {
char name[30];
char phonenum[10];
int age;
};
#endif
-
7/30/2019 Translating C++ Code Into Delphi
40/46
Classes and inheritance in C++Translating C++ Code into Delphi Pascal
A class is similar to a structure, with the difference that a class includes both private and
protected data and functions which manipulate the data to perform a specific function. It is
typical for these classes, like structures, to be defined in a header file, with the sources for the
functions included in a file with the same name with the difference the extension would be
'cpp' for the source and 'h' for the header file.
Normally the function of a class allows access to the results of manipulation of the data
while keeping the implementation private. For example a class might manipulate a set of
temperature variables and return energy as measurement of calories. The implementation of
the conversion and any variables these functions use would be private, and the result would be
public. In this way the actual source code which is also encapsulated in the class can be
changed while the public interface remains the same, and a caller need not be concerned with
the implementation of the embedded functions (only that the results returned are correct). A
large number of programmers could then contribute classes to a project and as long as the
calling routines understand the input variables and the public output variables of the class the
actual implementation does not need to concern any other programmer in the project (a
similar principle applies to DLLs which have a public interface, but, because they consist of
machine code, have a private implementation.)
The public section of a class consists of the interface, the part of the class that will be
visible to other programmers who actually use the class. This is kept separate from the
private section of the class, which is invisible to the user and the sole responsibility of the
programmer who designed the class. The protected section consists of that part of the class
that can be 'inherited' by a descendant class.
In C++ classes are declared with the class keyword...The following is an attempt at a verysimple example. (In this example I have decided to make the BIOS on the motherboard
'protected' rather than private....)
class AComputer {
public :
AComputer();
bool OnSwitch;
bool ResetButton;
char Keyboard;
bool Bootup(int partition);
protectedvoid BIOS();
private
char Keypressed;
int ScreenPos;
char GetKey();
void WriteToScreen;
}
Etc., etc... The user here, like most computer users, only need to understand how to turn
the computer on, and type on a keyboard, and doesn't need to be concerned about the 'private'
implementation of the inner workings of the machine (only that the machine does in factwork, and that when a key is pressed on the keyboard it does in fact show up on the screen.)
-
7/30/2019 Translating C++ Code Into Delphi
41/46
C++ classes, like Delphi classes, have a constructor, which is called when an actual
instance of the class is created. A default constructor will be called if none is specified, but if
you want to initalize the class a custom constructor is required. In the example above the
constructor is 'AComputer()' and has the same name as the class (there is no 'constructor'
keyword). The constructor can use function overloading. Functions are overloaded when
more than one function exists with exactly the same name, but the variable declarations are
different. The compiler keeps track of which version of the overloaded function is being
called by comparing the variables being passed in the code. If you take a look inside a DLL -
or a C++ DLL interface, you will see 'mangled names', since no two functions can have the
same name in C++. For example a function might look like the following in code...
int Add(int x, int y);
float Add(float x, float y);
The compiler will mangle the names (usually a random looking mixture of garbage
characters). You will see the mangled name in the interface section of the DLL code. CallingAdd with two integers calls the first function and calling with floats will result in a call to the
second function. Constructors can be overloaded in the same way. A default constructor
need take no parameters and overloaded versions of the constructor are distinguished by the
parameters they accept, as illustrated above.
Actual instances of a class are created in the same way that variables are created.
int x;
X is declared to be of integer type (notice that the syntax is backwards compared to
Delphi).
AComputer ThisComputer;
The members of the class can be accessed using the dot operator. Let's assume that the
class AComputer includes a public integer variable declared as 'y'. (If 'y' is not public then the
following code would generate a compiler error.)
int x = ThisComputer.y;
ThisComputer is an instance of the class AComputer. In this the default constructor is used
(either a default constructor, taking no parameters, supplied by the programmer, or the defaultconstructor provided by the compiler). If parameters are supplied then one of the overloaded
constructors will be called.
AComputer ThisComputer(16, 2, 9);
Here we assume that a constructor exists which accepts these parameters. Constructors are
never called directly, but rather are called automatically when an instance of a class is actually
created in code.
The destructor is called just before an instance of a class is destroyed (goes out of scope, if
the class was declared like a varable and is thus on the program stack, or is destroyed usingdelete, if the class was created dynamically and thus is on the heap). Once again there is no
-
7/30/2019 Translating C++ Code Into Delphi
42/46
keyword 'destructor' but rather the class name is used, preceded by tilde ( ~AComputer() ).
Destructors cannot be overloaded. If a destructor is not provided the compiler will provide a
default destructor. One common use for a destructor is to prevent memory leaks by releasing
any memory that was used by the instance of the class, but depending on the class
implementation, a destructor may be called on to do any other final operations (save data,
etc.)
The functions encapsulated in a class can access all the data and functions in a class
whether it is public or private, so long as an instance of the class exists. Each instance of a
class, however, exists seperately in memory (so the data values would be separate for each
instance, unless declared using the 'static' keyword, which is a way of declaring global
variables for the class - in this case all instances would share the static variables. However,
because an instance of the class could be expected to exist within the high speed cache, and
the global variables could be anywhere in memory, the possibility exists that using static
global variables in multiple instances of a class could cause cache misses. I just mention this,
since I have heard talk of how modern computers with a CPU memory cache choke a lot on
global variables due to the high penalties caused by cache misses. There is also something to
be concerned about when one instance of a class changes a global variable which then isaccessed by another instance of the same class - this problem is similar to that faced by multi-
threaded programs which need to co-operate when sharing common data...)
Typically the 'public' functions of a class are the outside user interface, and the actual
implementation of the task performed by the class is hidden. These public functions could
accept parameters and return results, either as the result of the function or as results included
in the public variables declared for the class. It should be noted here, that these public results
stored in variables are only 'in scope' and thus usable, so long as the class is in scope and
actually exists. If the class is destroyed or goes out of scope, so do these public variables.
A class is typically used in a program in three parts. There is the header file, the source filefor the functions of the class, and then there is the program code which actually uses the class.
For example you might see the following for the AComputer class)...
(acomputer.h - the header file)
#ifndef acomputerH
#define acomputerH
constant strings may be defined in the header file for the class (whever the compilerencounters the string SIZE it will substitute '22' in the source code ... it is typical for constants
to be in uppercase but this is not a strict requirement (just remember that, unlike Delphi, C++
is case sensitive...)
#define SIZE 22
#define LENGTH 13
The class definition would then follow in the header...
class AComputer {
public :...public variables and access functions ...
-
7/30/2019 Translating C++ Code Into Delphi
43/46
protected
.....
private
.... the internal variables and workings of the class hidden from public access...
};
#endif;
In a seperate source file the actual definitions of the functions declared in the header file
will be found with the same name as the header, but ending with 'cpp'...
acomputer.cpp ...
The include statements will be found at the head of the cpp file, and typically look
something like this - note I am unclear as to why '
-
7/30/2019 Translating C++ Code Into Delphi
44/46
Note that there is no semicolon after the include statement for the acomputer.h header file..
Next there follows a list of the functions included in this particular source file (note that
this is different from the source code for the class since the functions are declared in the
header file which is then included in the source file..) Let's assume that our program has the
typical 'main' file (which is not declared in the list of functions at the top of the file) and also
two functions called 'thisone' and 'thatone' ... The list of function declarations is followed by
the actual description of the code, starting below with the 'main' function...
int thisone(int x);
int thatone(int y);
void main(int whatever)
{
main code starts here...blah blah
}
int thisone(int x)
{code for this one..
}
etc., etc. ...
In code we want to create an instance of the class. We could do this using pointers.
AComputer* ThisComputer = new AComputer(constructor variables here);
The members of the class can then be accessed using pointer coding...Let's suppose the
class had a public function 'CPUTemp' which returned an integer...
int currenttemp = ThisComputer->CPUTemp();
Public variables can also be accessed using the same pointer syntax...
Because this instance of the class was created dynamically (on the heap) when we delete it
we are also calling the destructor for the class...
delete ThisComputer;
You might see code that inherits from a class and so I include a brief discussion of
inheritance from classes here as well...
Let's suppose that we want to 'inherit' code from the AComputer class to make a new class
called 'MacComputer'. The base class would be 'AComputer' and the derived class would be
'MacComputer'. We would indicate that inheritance is at work in the following way...
class MacComputer : public AComputer {
public
MacComputer(maybe some variables here); ...note that this is the constructor...
virtual char GetKey(some vars); ...note this is 'virtual' so we will 'override' the original
Acomputer code by writing a new routine for MacComputer ...
protectedetc...
-
7/30/2019 Translating C++ Code Into Delphi
45/46
private
virtual int something(variables); another routine is virtual and will be replaced for
MacComputer
The descendant class would then call the constructor of the base class in its constructor
code.
MacComputer::MacComputer(int x) :
AComputer(int x)
{
constructor for MacComputer code is here
}
If the default constructors provided are used then this would not be neccesary.
If we wanted to literally inherit anything from AComputer then the virtual keyword is not
used and the original code is simply inherited from the base class. We can also add newroutines not included in the orignal class by declaring them without using the virtual keyword
(which is not needed since in the case of a new function or variable it is not necessary to
indicate that we overriding the code in the base class). Note that the virtual keyword must be
included for those routines in the header file of the original base class 'AComputer' and the
declaration and the variables must match in the base class and in the derived class (the header
of the two functions must match) and then the function can be rewritten in the derived class.
Because the derived class provides its own version of this 'virtual' function this indicates to
the compiler that the derived (virtual) version of this function is to be called in all derived
classes, and not the original function by that same name in the base class.
The functionality of the original virtual can also be included in the new (virtual) functionfollowed by and/or preceded by additional code in the source file in the following way.
char MacComputer::GetKey(some variables)
{
some extra mac code here...
AComputer::GetKey(some variables); ...include the base code here...
now add some more mac code...
}
Note that 'GetKey' must be in the protected section of the AComputer code and not the
private section. What is in the protected section can be accessed by descendants that haveinherited from a class but what is in the private section even descendants cannot access, so
this would not work, and indicates one of the key differences when designing a class between
putting something into the private section or the protected section of the class. The
descendants of this class would then access the 'private code' in the same way as any other
outsider (through the interface provided by the base class). So 'private' functions and data in
a class are 'hidden' as are protected functions and data, but those that are protected are
available for inheritance.
Note that it is not required to call elements in the base class using the 'scope operator' if the
elements have not been over ridden. For example we must call AComputer::GetKey(blah)
because GetKey has been over ridden. Let's suppose that we inherit (without overriding)something called GetMouse(blah). We could simply use GetMouse (rather than
-
7/30/2019 Translating C++ Code Into Delphi
46/46
AComputer::GetMouse(blah) ) because there is no need to indicate the scope in this case,
since there is only one 'GetMouse' to consider. If the scope operator is not used, and a
function has been over ridden, then to avoid ambiguity the compiler uses the descendant
function, since it is considered to be more 'in scope' than the 'further away' ancestor or base
class.
If we wanted to use 'MacComputer' as the base class for, say, 'Mac64Computer' then it
would turn out to be the case that Mac64Computer would ultimately be a descendant of
AComputer as well. Thus inheritance can be thought of as being like a tree, with a root base
class, and many branches coming off of this root in a tree like structure.
It is also possible for descendant classes to use multiple inheritance. For example let's
suppose that a new computer wa