Major Differences Between the Syntax of C++ and Object Pascal

From a C++ Programmer's Point of View
 
by Rob Locher

This white paper with the academic-sounding title is intended to be a quick reference guide in tabular format for a C++ programmer trying to learn Delphi's Object Pascal.  (To be more specific, my background is in Visual C++ 6 and I am learning Delphi 3.)  It may also be useful in the other direction.  While I do take the time to mention a few common pitfalls, this document is obviously not intended to single-handedly teach one language or the other, to get bogged down in finer points, or to debate pros and cons.  If you have anything to add to this document, email me and I will be happy to consider it; this is a work in progress.  All copyrights and trademarks still belong to their rightful owners, especially for the icons.
 
 

Table of Contents
Miscellaneous
Fundamental Number Variable Types
Language Statements
Object-Oriented Features
Operators
Function Keys and Other IDE Tricks
Compiler Quirks
Appendix I: How to Create a Console App in Delphi
Appendix II: Links
Appendix III: Sources








Miscellaneous

 
Feature C++ Object Pascal
case-sensitive yes no
comments /* comment */ or 
// comment to end of line
{ comment } or 
(* comment *)
// comment to end of line
string constants "a string constant" (Note: zero based) 'a string constant' (Note: one based)
string constants with quotes "I said \"Hello\" to you." 'it''s a beautiful day!'
string escape sequences "hello\n" 'hello'#13
compiler passes two one
expression evaluation left to right right to left

Back to the top


Fundamental Number Variable Types

Variable types are subject to size changes with new compiler versions or OS versions.  (This is why in Visual C++ 6 int and long are identical.)  These sizes are for Visual C++ 6 and Delphi 3 on Windows 95/98/ME or Windows NT 4/2000 (Intel versions).

I left the umpteen character and string types out of this table on purpose.
 

Variable Size and Type Visual C++ Delphi 3
boolean bool (native type) or BOOL (Win32 API) Boolean
1 byte signed integer signed char ShortInt
1 byte unsigned integer char *, unsigned char Byte
2 byte signed integer short SmallInt
2 byte unsigned integer unsigned short Word
4 byte signed integer int or long LongInt or Integer
4 byte unsigned integer unsigned int or unsigned long or DWORD (Win32 API) n/a
4 byte floating point float Single
6 byte floating point n/a Real **
8 byte floating point double or long double Double
10 byte floating point n/a Extended

* I wouldn't use char as a number type because it is unclear (and in fact can be changed with the compiler switch /J in Visual C++ 6) whether it is signed or not.

** Real isn't implemented in Intel hardware, and is only there for backward compatibility.  Don't use it!
 
 

Back to the top


Language Statements

 
Statements C++ Object Pascal
if ... else
(Note the difference in the equality operator!)
if (7 == x)
  y = 5;  // must have semicolon
else  // else is separate stmt
  y = 7;
if 7 = x then
  y := 5 // ";" illegal here
else  // part of if..then..else
  y := 7;
if ... else if if (7 == x)
  y = 5;  // has ";"
else
{
  if (9 == x)
    y = 8;  // has ";"
  else
    y = 11;
}
/* Note that "if (7 == x)" is preferable to "if (x == 7)".  The compiler would flag the error if you accidentally put "if (7 = x)", but not if you put "if (x = 7)", which would cause a difficult to catch logic error. */
if 7 = x then
  y := 5  // no ";"
else
  if 9 = x then
    y = 8  // no ";"
  else
    y = 11;
for for (i = 0; i < 7; i++) for i := 0 to 6 do
while while (n == 7) while n = 7 do
do...while / repeat...until
(Note that the test conditions are logically opposite.)
do
...
while (n == 7);
repeat
...
until n <> 7;
switch / case switch (n)
{
  case 0 : str = "alpha";
           break;
  case 1 : str = "beta";
           break;
  case 2 : str = "gamma";
           break;
  default: str = "invalid";
}  // switch
case n of
  0: str := 'alpha';
  1: str := 'beta';
  2: str := 'gamma';
else
  str := 'invalid';
end;  // case
enumerated type declaration enum colors {cyan, magenta, yellow}; type
  colors = (cyan, magenta, yellow);
arrays int arr[50];
// bounds start at zero
arr = array [5..54] of integer;
// can use any bounds
set types n/a type
  col_t = (red, blu, grn);  // enum.
  MySetType := set of col_t;
var
  a, b, c : MySetType;
begin
  a := [];  // empty set
  a := a + [red];  // union
  b := a - [blu]; // difference
  c := a * b; // intersection
  if [red] in a then ...
(* Note that sets are generally used to handle non-exclusive flags in Object Pascal. *)
pointer stuff int i = 5;
int * pi;  // declare a pointer
pi = &i;  // address operator
*pi = 6;  // dereference; *pi is same as i
i: integer;
pi: ^integer;  // declare a pointer
pi^ = 6;  // dereference
dynamic memory allocation int * pi = NULL;
int * parri = NULL;
// create pointers first
pi = new int;  // allocate mem
parri = new int[50];
...
// Note: must use proper delete
// operator!
delete pi;
delete[] parri;
var
  p : ^integer;
begin
  new(p);
...
dispose(p);
(* Note that dynamic allocation is rare in Object Pascal, due to the reference model for classes. *)
structures / records struct s_t
{
  int amt;
  char payee[100];
} s;  // declaration of s
      // is optional here

s_t s2;

type
  s_t = record
    amt: integer;
    payee: string;
  end;  // record

var
  s, s2: s_t;

function with value (copied) parameters int somefunction(int);
// declaration

int somefunction(int m)  // no ";"
{
  // definition
  ...
  return 7;
}

function somefunction(m: integer) : integer;  // semicolon
begin
  ...
  result := 7;  // using result
  // or somefunction := 7;
end;
(* Note: using "result" is preferred because it eliminates accidental recursion *)
procedure with reference parameters (and constant parameters) void someprocedure(int &, const int &);

void someprocedure(int &m, const int n)
{
  ...
}

procedure someprocedure (var m: integer, const n: integer);
begin
  ...
end;
exception handling void somefunction(void)
{
  try
  {
    int z = riskycode();
  }
  catch (const SomeExcptnClass * e)
  {
    cout << "caught an error!";
  }
  // execution continues here
  // after try block
  ...
}

int riskycode(void)
{
  SomeExcptnClass ec;
  if (...)  // a grievous error
    throw ec;
}

/* Note: uncaught exceptions by default terminate the program unless a default exception handler has been registered. */
 

procedure someproc;
var
  z : integer;
  sec : SomeExceptionClass;
begin
  try
    z := riskycode();
  finally
    // code which must be executed
    // even if exception caught --
    // no C++ direct equivalent
    ...
  end;  // inner try block

// or

  try
    z := riskycode();
  except
    on e : SomeExcptnClass do
      Writeln('caught an error!');
  end;
  (* execution continues here
  after try block *)
  ...
end;

function riskycode : integer;
var
  ec : SomeExcptnClass;
begin
  if ... then  // a grievous error
    raise ec;
end;

(* Note: Uncaught exceptions are handled by Delphi by default, which pops up a dialog and then continues.

Note: it is illegal to have both an except and a finally statement after a try block, which I find very odd.

try ... except is actually rare in common practice.  More common is try ... finally, which is used to insure that cleanup gets done even if an exception gets raised.

If you *need* both an except and a finally block, use nested try loops. See "Mastering Delphi 3" page 244. *)

Back to the top


Object Oriented Language Features

 
Feature C++ Object Pascal
class declaration class myclass
{
public:
  int get_m(void);
  void set_m(int);
protected:
  int m;
}; // semicolon is mandatory

int myclass::get_m(void)
{
  ...
}

// etc.

type
  myclass = class
    function get_m : integer;
    procedure set_m(m : integer);
    m : integer;
  end;

function myclass.get_m : integer;
begin
  ...
end;

// etc.

creating an instance of an object myclass anobject;

/* Note that creation and destruction are handled by the class. */

/* A calling program can only refer to an instantiated object, never a class. */

anobject := myclass.Create;  // see 1st note
... // use it
anobject.Free;

(* Note you must refer to the CLASS when you create an instance of the OBJECT, or you will surely meet the dreaded General Protection Fault. *)

(* Note: creation and destruction must be done explicitly by the CALLER. *)

class constructors class someclass
{
public:
  someclass(int a); /* constructor */
  ~someclass(void); /* destructor */
};
type
  someclass = class
    constructor Create(a: integer);
    destructor Free;
  end;
(* Note that there is nothing sacred about the names Create and Free.  It is just a convention to use the same names as the default constructor and destructor inherited from TObject. *)
derived classes class someclass : public baseclass
{
  ...
};

/* C++ classes have no automatic common ancestor. */

type
  someclass = class (baseclass);
begin
  ...
end;

(* Note that all classes ultimately derive from TObject, whether your base class is declared so or not. *)

(* Object Pascal has no private or protected class derivation. *)

calling base class constructors and destructors from a derived class (neglect at your peril!) // in the .h file
class someclass : public baseclass
{
  /* base constructor, destructor are virtual */
  someclass(int a);
  ~someclass();
  ...
};

// in the .cpp file
someclass::someclass(int a) : baseclass(a)
{
  ...  // some more initialization
}

someclass::~someclass();
{
  ...  // some more cleanup
  baseclass::~baseclass();
}

interface

type
  someclass = class (baseclass);
    constructor Create(a:integer); override;
    destructor Destroy; override;
    ...
  end;

implementation

constructor someclass.Create(a: integer);
begin
  inherited Create(a);
  ...  // some more initialization
end;

destructor someclass.Destroy;
begin
  ...  // some more cleanup
  inherited Destroy;
end;

virtual class methods class baseclass
{
  ...
  virtual void override_me(void);
  ...
};
type
  baseclass = class;
    ...
    procedure override_me; virtual;
    ...
  end;
private keyword not inherited, default for a class not inherited
protected keyword inherited inherited
public keyword inherited, default for a structure inherited
published keyword n/a available at design time, default for a class
current object identifier this pointer self object

Back to the top


Operators (Grouped in Order of Precedence in C++)

The numbers in the Operator column indicate precedence groups.  For example, the C++ operators & and * have the same precedence because they are in the same group, (3).  Not all operators are listed -- operators that are the same in both languages (multiply, add, etc.) are generally omitted.  I haven't researched Object Pascal enough to know if the operator precedence is the same.
 
Operator C++ Object Pascal
(1) scope resolution :: .
(2) indirect membership -> n/a
increment i++, ++i n/a
decrement i--, --i n/a
(3) logical NOT ! not
bitwise negation (one's complement) ~ not
address & @
dereference * ^
floating-point division / /
integer division / div
(5) modulus (remainder) ^ mod
set union n/a +
set difference n/a -
set intersection n/a *
(7) left shift (bitwise) << shl
right shift (bitwise) >> shr
(9) equal to (logical test) == =
not equal to != <>
(10) bitwise AND & and (* see note)
(11) bitwise XOR ^ xor
(12) bitwise OR | or (* see note)
(13) logical AND && and
(14) logical OR || or
(15) conditional (shorthand if statement) n > 7 ? b = 5 : b = 10; n/a
(16) simple assignment = :=
add and assign, subtract and assign, etc. +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>= n/a
(17) combine two expressions into one , n/a

Note that the and and or operators cause problems for new Object Pascal programmers coming from C++.  The problem is that and and or are used for both the logical and the bitwise versions of the operators.  What this means is that a statement such as "if x > 5 and x < 7 then" will get you into trouble.  Why, you ask?  The reason is that the bitwise versions of the and and or operators have a high precedence in Object Pascal.  In the example, the first thing the compiler will do is attempt to evaluate "5 and x" using the bitwise version of and, which is not what you intended.  The solution is to use parentheses every time you use logical ands or ors in an if statement, like so: "if (x > 5) and (x < 7) then".
 
 

Back to the top


Function Keys and Other IDE Tricks

There is not an exact correspondence between the function keys in Visual C++ 6 and Delphi 3.  Because of this, people typically develop different ways of doing similar tasks in the two IDEs.
 
 
Task Visual C++ Delphi
save / compile / run F7 (build and save),  Ctrl+F5 (run ignoring breakpoints) or  F5 (run to next breakpoint) First, go to Tools -> Environment Options... and click on the Preferences tab.  Check "Editor files" under the "Autosave options" group and click Ok.

F9 (build if necessary, save if options set, run to next breakpoint)

compile only F7 (build and save) Ctrl+F9 (compile)
run only (ignore breakpoints) Ctrl+F5 (run) n/a -- use F9 (build if necessary, save if options set, run to next breakpoint)
run only (stop for breakpoints) F5 (go) use F9 (build if necessary, save if options set, run to next breakpoint)
object inspector n/a F11
pull up the form Resources tab of the workspace pane F12
view all files in a project FileView tab of the workspace pane Project Manager
pick a particular file FileView tab of the workspace pane Ctrl+F12
pick a particular form Resources tab of the workspace pane Shift+F12
find a reference to a variable source browser (Alt+F12) symbol browser
find a function (or procedure) ClassView tab of the workspace pane n/a -- use symbol browser
view class framework hierarchy, including user-defined derived classes n/a (use help) View->Browser...
go to code definition of a keyword right-click, Go To Definition Of ... Ctrl+(click on keyword) (Delphi 4 or later)
set / clear breakpoint F9 click in the gutter next to the line of code
trace into F11 F7
trace over F10 F8
trace out of Shift+F11 n/a
run to cursor Ctrl+F10 F4

Back to the top


Compiler Quirks

In both Visual C++ (version 6) and Delphi (versions 3 - 5), each compiler has strange quirks to how it processes a file.  Since these seem to be poorly documented, I will mention them here.

In Visual C++, if the Project Options say that the file being compiled uses precompiled headers, don't put anything but comments and blank lines before the #include "stdafx.h" directive.  Any such lines will be ignored.  Also, in an MFC application, be careful about modifying any of the weird comment lines put there by the Class Wizard, or any of the lines in a block of code delimited by the weird comments.  If you make any such modifications, you will break the Class Wizard.  The only exception I know of is that you may freely change the value to which a variable is initialized inside a block of code delimited by the wizard's comments.  I recommend you use the Class Wizard to make changes wherever possible.  It will prompt you to manually delete a block of code if necessary, for example if you decide to remove an event handler.

Delphi is kinder to your source code, in that it doesn't mark it up with unsightly comments.  However, make no mistake, the compiler makes a clear distinction (which unfortunately is invisible to you in the editor) between lines of code you write and lines of code it writes.  It will normally maintain lines of code it inserts; for example, if you save the file, empty function and procedure declarations inserted when you double-clicked something to create an event handler go away.  If you modify Delphi's code, on the other hand, Delphi assumes you have taken over maintenance for those lines from now on.  This will probably cause you problems.  The general rule is, if you don't modify Delphi's code, Delphi won't modify your code.  Delphi's code includes function and procedure stubs for event handlers.  It also includes all the code from the top of the file, to the last line of the section of the declaration for the form class that isn't declared public or private (etc.).  In this example,

...
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
  private
everything up to the "private" line is Delphi's code.

In both Visual C++ and Delphi, remember that Undo is your friend, and hopefully these quirks won't cause you to come to grief.
 
 

Back to the top



 

Appendix I: How to Create a Console App in Delphi 3

I am of the opinion that console apps are an essential tool of every programmer, even a guru, if for no other purpose than to test your helper classes and such.  The creators of Delphi apparently don't much agree with me, because there is no obvious way to create a bare-bones Delphi console app in Delphi 3.  (It is pretty simple in Delphi 5.)  Still, it can be done.
  1. Create a new project in Delphi.
  2. Go to Project -> Options... , choose the Linker tab, check the "Generate console application" box, and click Ok.
  3. In your Pascal unit, comment out the two lines that say "var" and "Form1: TForm1;".  Compile your application with Ctrl-F9 or just F9.  Delphi will report an error and open your project file as a text document. (Alternately, to view the source code version of your project file you could have selected View -> Project Source.)  The offending highlighted line of code will say "Application.CreateForm(TForm1, Form1);".  Comment out this line.  (If you don't comment it out, when you run the application you will have a blank form pop up after your console closes.)
  4. You might think that you could clean up your code by deleting the TForm1 class declaration in your unit.  Unfortunately, that doesn't work because Delphi is strict about requiring at least one form, even if an instance of the form is never created and the form is never used.  As far as I can tell, you still need the "uses" declaration line, though it might be possible to cut out some of the units listed.
  5. You should now be able to compile and run your application.  Here is some code to get you started:
    1.  
      begin
        Writeln('Hello World!');
        Writeln('Press <enter> to exit');
        Readln;
      end.
Back to the top


Appendix II: Links

I found an excellent page that sets out to do the same thing this page does, but not in quite such a tabular format.  It does a much better job of discussing the differences, and is wonderfully unbiased.  Click here

Here's an article which is extremely biased towards Delphi (check out the background image) and dead wrong about much about Visual C++.  I only include the link because it does make a few valid points (along with many invalid ones), and out of a sense of fairness.  Click here: you've been warned

Back to the top


Appendix III: Sources

C++ Primer Plus, 2nd Edition, by Stephen Prata.  ISBN 1-878739-74-3

Mastering Delphi 3, 2nd Edition, by Marco Cantù.  ISBN 0-7821-2052-0

Back to the top
 

Copyright © 2001 Robert Locher.  All rights reserved.

er>Back to the top


Appendix II: Links

I found an excellent page that sets out to do the same thing this page does, but not in quite such a tabular format.  It does a much better job of discussing the differences, and is wonderfully unbiased.  Click here

Here's an article which is extremely biased towards Delphi (check out the background image) and dead wrong about much about Visual C++.  I only include the link because it does make a few valid points (along with many invalid ones), and out of a sense of fairness.  Click here: you've been warned

Back to the top


Appendix III: Sources

C++ Primer Plus, 2nd Edition, by Stephen Prata.  ISBN 1-878739-74-3

Mastering Delphi 3, 2nd Edition, by Marco Cantù.  ISBN 0-7821-2052-0

Back to the top
 

Copyright © 2001 Robert Locher.  All rights reserved.