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
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 |
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!
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
|
function with value (copied) parameters | int somefunction(int);
// declaration int somefunction(int m) // no ";"
|
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)
/* 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
function riskycode : integer;
(* 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. *) |
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;
// 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();
|
interface
type
implementation constructor someclass.Create(a: integer);
destructor someclass.Destroy;
|
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 |
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".
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 |
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,
...everything up to the "private" line is Delphi's code.
type
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
private
In both Visual C++ and Delphi, remember that Undo is your friend, and
hopefully these quirks won't cause you to come to grief.
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
Mastering Delphi 3, 2nd Edition, by Marco Cantù. ISBN 0-7821-2052-0
Copyright © 2001 Robert Locher. All rights reserved.