Académique Documents
Professionnel Documents
Culture Documents
Contents
Introduction ....................................................................................................................................6
Preliminaries ...................................................................................................................................7
Programs ..............................................................................................................................7
The Development Environment .........................................................................................8
Compiling the Source Code ................................................................................................8
A Simple C++ Program ......................................................................................................9
Compile Errors ..................................................................................................................11
Using Comments................................................................................................................12
The ANSI Standard...........................................................................................................13
Identifiers ...........................................................................................................................13
Memory ..............................................................................................................................14
Simple Input/Output ....................................................................................................................16
Printing output ..................................................................................................................16
Reading input.....................................................................................................................19
Variables........................................................................................................................................21
Variable Names .................................................................................................................21
Data types and sizes...........................................................................................................21
Numeric Variable Types ..........................................................................................21
Integer Numbers.......................................................................................................24
Real Numbers...........................................................................................................25
Characters ................................................................................................................25
Strings ......................................................................................................................26
Symbolic Constants ...........................................................................................................28
Using #define directive ............................................................................................28
Defining Constants with the const Keyword ...........................................................29
Operators and expressions...........................................................................................................32
The Assignment Operator ................................................................................................32
Arithmetic Operators........................................................................................................33
Delia Ungureanu - UNITBV
Contents
Bitwise Operators..............................................................................................................36
Relational Operators.........................................................................................................37
Logical Operators..............................................................................................................39
Conditional Operator........................................................................................................39
Compound Assignment Operator....................................................................................40
Comma Operator ..............................................................................................................41
Parentheses Operator .......................................................................................................42
Operator Precedence.........................................................................................................42
Type Conversion................................................................................................................43
Statements .....................................................................................................................................45
Statements and White Space ............................................................................................46
Null Statements..................................................................................................................46
Compound Statements ......................................................................................................46
The if Statement ................................................................................................................48
The else Clause ........................................................................................................49
The switch Statement ........................................................................................................51
Controlling Program Execution.......................................................................................53
The for Statement.....................................................................................................54
Nesting for Statements.............................................................................................56
The while Statement ................................................................................................57
The do...while Loop .................................................................................................58
The continue Statement............................................................................................60
The break Statement ................................................................................................60
The return Statement................................................................................................61
Pointers ..........................................................................................................................................62
Pointers and Symbolic Constants ....................................................................................65
Pointer Arithmetic.............................................................................................................65
Consequences of use of uninitialized pointers ................................................................67
Arrays ............................................................................................................................................68
Single-Dimensional Arrays...............................................................................................68
Multi-Dimensional Arrays................................................................................................70
Dynamic Memory .........................................................................................................................73
Delia Ungureanu - UNITBV
Contents
References......................................................................................................................................75
Functions .......................................................................................................................................76
Parameters and Arguments..............................................................................................79
Symbolic Constants ...........................................................................................................83
Global and Local Scope ....................................................................................................84
Scope Operator ..................................................................................................................85
Auto Variables ...................................................................................................................86
Register Variables .............................................................................................................86
Static Variables and Functions ........................................................................................87
Extern Variables and Functions ......................................................................................88
Runtime Stack ...................................................................................................................89
Inline Functions .................................................................................................................90
Recursion............................................................................................................................91
Default Arguments ............................................................................................................92
Overloading Functions......................................................................................................94
Creating New Types .....................................................................................................................97
Typedef ...............................................................................................................................97
Enumerations.....................................................................................................................98
Structures.........................................................................................................................100
Defining and Declaring Structures.........................................................................100
Accessing Structure Members ...............................................................................101
Initializing Structures.............................................................................................102
Pointers to Structures .............................................................................................102
Structures That Contain Structures ........................................................................103
Structures That Contain Arrays .............................................................................106
Pointers as Structure Members ..............................................................................107
Pointers and Arrays of Structures ..........................................................................108
Passing Structures as Arguments to Functions ......................................................110
Unions...............................................................................................................................111
Bit Fields...........................................................................................................................113
Object-Oriented Programming .................................................................................................115
Encapsulation .........................................................................................................115
Delia Ungureanu - UNITBV
Contents
Inheritance..............................................................................................................115
Polymorphism ........................................................................................................116
Classes ..............................................................................................................................116
Declaring a Class ...................................................................................................117
Implementing Class Methods ................................................................................118
Inline Implementation ............................................................................................119
Defining an Object.................................................................................................120
Accessing Class Members .....................................................................................120
Pointers to Classes .................................................................................................120
this Pointer .............................................................................................................122
Constructors ...........................................................................................................123
Destructors .............................................................................................................126
Static Members ......................................................................................................127
Friends....................................................................................................................128
Overloading......................................................................................................................129
Operator Overloading ............................................................................................130
Type Conversion..............................................................................................................133
Constructor conversion ..........................................................................................134
Operator conversion ...............................................................................................134
Inheritance ..................................................................................................................................136
Type Conversion..............................................................................................................140
Intro ductio n
The only way to learn a new programming
language is by writing programs in it.
- Brian Kernighan
This book is designed to help you teach how to program with C++.
You don't need any previous experience in programming to learn C++ with this presentation.
This book starts you from the beginning and teaches you both the language and the concepts
involved with programming C++. You'll find the numerous examples of syntax and detailed
analysis of code.
You'll learn about such fundamentals as managing I/O, loops and arrays, object-oriented
programming, templates, and creating C++ applications.
This book does not attempt to cover the difficult topic of Windows programming because we
believe you need to know the basics of programming first.
Lessons provide sample listings and complete with sample output and an analysis of the code.
To help you, each lesson ends with a set of common questions and answers, exercises, and a quiz.
You can check your progress by examining the quiz and exercise answers provided in the
course's appendix.
This course offers notions about computer programming using structural languages, procedural
languages, with examples in C++ programming language. It strive, also, that students obtain
abilities for programming through knowledge of data structures and programming techniques
concomitant with development of an application. It offers too basic notions about object oriented
programming.
Prelimina ries
Programs
The word program is used in two ways: to describe individual instructions, or source code,
created by the programmer, and to describe an entire piece of executable software. This
distinction can cause enormous confusion, so we will try to distinguish between the source code
on one hand, and the executable on the other.
A program can be defined as either a set of written instructions created by a
programmer or an executable piece of software.
Source code can be turned into an executable program in two ways: Interpreters translate the
source code into computer instructions, and the computer acts on those instructions immediately.
Alternatively, compilers translate source code into a program, which you can run at a later time.
While interpreters are easier to work with, most serious programming is done with compilers
because compiled code runs much faster. C++ is a compiled language.
Computer programs are composed of instructions and data. Instructions tell the computer to do
things, such as to add and subtract. Data is what the computer operates on, such as the numbers
that are added and subtracted. In mature programs, the instructions don't change as the program
executes (at least they're not supposed to). Data, on the other hand, can and usually does change
or vary as the program executes. A variable is nothing more than the name used to point to a
piece of this data.
C++, perhaps more than other languages, demands that the programmer design the program
before writing it. Trivial problems, such as the ones discussed in the first few chapters of this
book, don't require much design. Complex problems require design. A good design also makes
for a program that is relatively bug-free and easy to maintain.
The first question you need to ask when preparing to design any program is, "What is the
problem I'm trying to solve?" Every program should have a clear, well-articulated goal.
A solution to a problem is called an algorithm. It describes the sequence of steps to be performed
for the problem to be solved.
To be intelligible to a computer, an algorithm needs to be expressed in machine language, a
language understood by it. Programs expressed in the machine language are said to be
executable. A program written in any other language needs to be first translated to the machine
language before it can be executed.
A machine language is too cryptic to be used directly by the programmers. A further abstraction
of this language is the assembly language which provides mnemonic names for the instructions
and a more intelligible notation for the data. An assembly language program is translated to
machine language by a translator called an assembler.
Delia Ungureanu - UNITBV
Preliminaries
The assembly languages are difficult to work with, too. High-level languages such as C++
provide a much more convenient notation for implementing algorithms. A program written in a
high-level language is translated to assembly language by a translator called a compiler. The
assembly code produced by the compiler is then assembled to produce an executable program.
Preliminaries
Figure 1. C++ Compilation
#include <stdio.h>
int main (void)
{
cout << "Hello World !\n";
return 0;
}
Comments:
1 This line uses the preprocessor directive #include to include the contents of the
header file iostream.h in the program. Iostream.h is a standard C++ header file
and contains definitions for input and output.
2
Preliminaries
- The last character in this string (\n) is a newline character which is similar to a
carriage return on a type writer. A stream is an object which performs input or
output. Cout is the standard output stream in C++ (standard output usually means
your computer monitor screen).
- The symbol << is an output operator which takes an output stream as its left
operand and an expression as its right operand, and causes the value of the latter
to be sent to the former. In this case, the effect is that the string "Hello World\n"
is sent to cout, causing it to be printed on the computer monitor screen.
This brace marks the end of the body of main.
The preprocessor runs before your compiler each time the compiler is invoked. The
preprocessor translates any line that begins with a pound symbol (#) into a special
command, getting your code file ready for the compiler.
A string is any sequence of characters enclosed in double-quotes, like Hello world.
A stream is an object which performs input or output. Cout is the standard output
stream in C++ (standard output usually means your computer monitor screen).
Compilation of source file FIRST.CPP produces a number of steps (most of which are transparent
to the user):
First, the C++ preprocessor goes over the program text and carries out the
instructions specified by the preprocessor directives (e.g., #include). The result
is a modified program text which no longer contains any directives.
Then, the C++ compiler translates the program code and provides the
FIRST.OBJ file. The outcome may be incomplete due to the program referring to
library routines which are not defined as a part of the program. For example,
Listing 1 refers to the << operator which is actually defined in a separate IO
library.
Finally, the linker completes the object code by linking it with the object code of
any library modules that the program may have referred to. The final result is an
executable file FIRST.EXE.
Hello World !
10
Preliminaries
Compile Errors
Unfortunately, almost every program, no matter how trivial, can and will have errors, or bugs, in
the program. Some bugs will cause the compile to fail, some will cause the link to fail, and some
will only show up when you run the program.
Whatever type of bug you find, you must fix it, and that involves editing your source code,
recompiling and relinking, and then rerunning the program.
Good compilers will not only tell you what you did wrong, they'll point you to the exact place in
your code where you made the mistake. The great ones will even suggest a remedy!
The Listing 2 is a demonstration of compiler error:
Listing 2
1
2
3
4
5
#include <iostream.h>
int main (void)
{
cout << "Hello World\n";
return 0;
Recompile your program and you should see an error that looks similar to the following dialog in
Message window:
Dialog 2
Compiling FIRST.CPP:
Err First.cpp 5: Compound statement missing terminating } in function
main().
Messages are written with the message class first, followed by the source file name and line
number where the error was detected, and finally with the text of the message itself.
Compiler Errors and Warnings
Compile-time error messages indicate errors in program syntax, command-line errors, or errors
in accessing a disk or memory.
When most compile-time errors occur, the compiler completes the current phase (preprocessing,
parsing, optimizing and code-generating) of the compilation and stops. But when fatal compiletime errors happen, compilation stops completely. If a fatal error occurs, you must fix the error
and restart compilation.
Run-time errors occur after the program has successfully compiled and is running. Run-time
errors are usually caused by logic errors in your program code. If you receive a run-time error,
you must fix the error in your source code and recompile the program for the fix to take effect.
Delia Ungureanu - UNITBV
11
Preliminaries
Warnings indicate that conditions which are suspicious but legitimate exist or that machinedependent constructs exist in your source files. Warnings do not stop compilation.
Linker Errors and Warnings do not make the linker stop or cause .EXE or .MAP files to be
deleted. When such errors happen, don't try to execute the .EXE file. Fix the error and re-link. A
fatal link error, however, stops the linker immediately. In such a case, the .EXE file is deleted.
Librarian errors and warnings occur when there is a problem with files or extended
dictionaries, when memory runs low, or when there are problems as libraries are accessed.
Using Comments
C++ comments come in two ways: the double-slash (//) comment and the slash-star (/*)
comment. The double-slash comment, which will be referred to as a C++-style comment, tells the
compiler to ignore everything that follows this comment, until the end of the line.
The slash-star comment mark tells the compiler to ignore everything that follows until it finds a
star-slash (*/) comment mark. These marks will be referred to as C-style comments. Every /*
must be matched with a closing */.
Many C++ programmers use the C++-style comment most of the time, and reserve C-style
comments for blocking out large blocks of a program.
As a general rule, the overall program should have comments at the beginning, telling you what
the program does. Each function should also have comments explaining what the function does
and what values it returns. Finally, any statement in your program that is obscure or less than
obvious should be commented as well.
Listing 3 demonstrates the use of comments, showing that they do not affect the processing of the
program or its output.
Listing 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
This is my first C++ program
*/
#include <iostream.h>
int main (void)
{
/* this is a comment
and it extends until the closing
star-slash comment mark */
cout << "Hello World\n";
// this comment ends at the end of the line
cout << "Hello World, again\n";
return 0; // this comment ends at the end of the line
}
12
Preliminaries
The comments on lines 1 through 3, 7 through 9 are completely ignored by the compiler, as
are the comments on lines 11, and 13. So, execution of this program produces Dialog 3.
Dialog 3
1
2
Hello World
Hello World, again
has
been
published,
and
link
is
available
at
The ANSI standard is an attempt to ensure that C++ is portable, that code you write for
Microsoft's compiler will compile without errors, using a compiler from any other vendor.
For most students of C++, the ANSI standard will be invisible. The standard has been stable for a
while, and all the major manufacturers support the ANSI standard.
Identifiers
Programming languages use names to refer to the various entities that make up a program. (i.e.,
variable names, function names, type names, and macro names, which will be described later in
this book).
Names are a programming convenience, which allow the programmer to organize what would
otherwise be quantities of plain data into a meaningful and human-readable collection. As a
result, no trace of a name is left in the final executable code generated by a compiler. For
example, a temperature variable eventually becomes a few bytes of memory which is referred to
by the executable code by its address, not its name.
C++ imposes the following rules for creating valid names (also called identifiers). A name
should consist of one or more characters, each of which may be a letter (i.e., 'A'-'Z' and 'a'-'z'), a
digit (i.e., '0'-'9'), or an underscore character ('_'), except that the first character may not be a
digit. Upper and lower case letters are distinct. For example:
distance
// valid identifier
dist1
// valid identifier
dist_1
// valid identifier
1_dist
distance
// valid identifier
Distance
123
13
Preliminaries
C++ imposes no limit on the number of characters in an identifier, but, to make the program
legible, it must be short and suggestive.
Certain words are reserved by C++ for specific purposes and may not be used as identifiers.
These are called reserved words or keywords and are summarized in Table 1:
Table 1 C++ keywords.
asm
auto
continue
default
float
for
new
operator
signed
sizeof
try
typedef
break
case
catch
char
class
const
delete
do
double
else
enum
extern
friend
goto
if
inline
int
long
private
protected
public
register
return
short
static
struct
switch
template
this
throw
union
unsigned
virtual
void
volatile
while
Memory
A computer uses random-access memory (RAM) to store information while it's operating. RAM
is located in integrated circuits, or chips, inside your computer. RAM is volatile, which means
that it is erased and replaced with new information as often as needed. Being volatile also means
that RAM "remembers" only while the computer is turned on and loses its information when you
turn the computer off.
This memory can be thought of as a contiguous sequence of bits, each of which is capable of
storing a binary digit (0 or 1). Typically, the memory is also divided into groups of 8
consecutive bits (called bytes). The bytes are sequentially addressed. Therefore each byte can be
uniquely identified by its address (see Figure 2). Addresses are assigned to memory locations in
order, starting at zero and increasing to the system limit.
Note: You don't need to worry about addresses; it's all handled automatically by the
C/C++ compiler.
Each computer has a certain amount of RAM installed. The amount of RAM in a system is
usually specified in kilobytes (KB) or megabytes (MB), such as 512KB, 640KB, 2MB, 4MB, or
8MB. One kilobyte of memory consists of 1,024 bytes (210 bytes). Thus, a system with 640KB of
memory actually has 640 * 1,024, or 65,536, bytes of RAM. One megabyte is 1,024 kilobytes. A
machine with 4MB of RAM would have 4,096KB or 4,194,304 bytes of RAM.
14
Preliminaries
Figure 2 Organization of memory
While the exact binary representation of a data item is rarely of interest to a programmer, the
general organization of memory and use of addresses for referring to data items (as we will see
later) is very important.
15
Simp le Input/Output
The most common way in which a program communicates with the outside world is through
simple, character-oriented Input/Output (IO) operations.
C++ does not, as part of the language, define how data is written to the screen or to a file, nor
how data is read into a program. These are clearly essential parts of working with C++, however,
and the standard C++ library now includes the iostream library, which facilitates input and
output (I/O).
When a C++ program that includes the iostream classes starts, four objects are created and
initialized:
cin handles
Note: Speaking about Input/Output we will use notions like variables, constants, data
types that are discussed later in this chapter.
Printing output
To send data to standard output, use the operator <<.
Note: C programmers know this operator as the bitwise left shift. C++ allows
operators to be overloaded. When you overload an operator, you give it a new
meaning when that operator is used with an object of a particular type. With iostream
objects, the operator << means send to.
For example:
cout << "cout example !";
sends the string cout example to the object called cout.
The special iostream function endl outputs the line and a newline. So Listing 3 and Listing 4 will
produce some result (Dialog 3)
16
Simple Input/Output
Listing 4
1
2
3
4
5
6
7
8
9
10
#include <iostream.h>
int main (void)
{
cout << "Hello World";
cout << endl;
cout << "Hello World, again";
cout << endl;
return 0;
}
Comments:
1 On this line the statement #include <iostream.h> causes the iostream.h file to be
added to your source code. This is required if you use cout and its related functions.
5
6
7
Inside a character string, you can insert special characters that do not print using escape
sequences. These consist of a backslash (\) followed by a special code. For example \n means
new line. Your compiler manual or local Standard C guide gives a complete set of escape
sequences; others include \t (tab), \\ (backslash) and \b (backspace).
An important feature is string concatenation. If two quoted strings are adjacent, and no
punctuation is between them, the compiler will paste the strings together as a single string. This is
particularly useful when printing code listings in books and magazines that have width
restrictions:
Listing 5
1
2
3
4
5
6
7
8
9
10
#include <iostream.h>
int main (void)
{
// Example of string concatenation
cout << "This string is far too long to put on a single "
"line but it can be broken up with no ill effects\n"
"as long as there is no punctuation separating "
"adjacent strings.\n";
}
17
Simple Input/Output
Comments:
On lines 6..9 an informative message is printed. It is create by string concatenation.
The effect of running of Listing 5 is Dialog 4
Dialog 4
1
2
3
This string is far too long to put on a single line but it can be
broken up with no ill effects
as long as there is no punctuation separating adjacent strings
String arguments and constant numbers are mixed in the cout statement. Because the operator <<
is overloaded with a variety of meanings when used with cout, you can send cout a variety of
different arguments. Floating-point numbers are determined automatically, by the compiler. In
addition, any character can be sent to a stream object using a cast to a character (a char is a data
type designed to hold characters), which looks like a function call: char( ), along with the
character's ASCII value. In the above program, an escape is sent to cout.
Listing 6
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream.h>
int main (void)
{
cout << "It will be printed different type of data:\n";
cout << "string: " << "abcd" <<endl;
cout << "char: " << 'a' << endl;
cout << "char: " << char(97) <<endl;
cout << "non-printing char (escape): "<< char(27) << endl;
cout << "integer: " << 123 <<endl;
cout << "long: " << -1234567 <<endl;
cout << "double: " << 123.456 << endl;
}
Comments:
6 Three values are passed to cout on line 6, and each value is separated by the
insertion operator. The first value is the string "string: ". Note the space after the
colon. The space is part of the string. Next value is a string too; it is passed to the
insertion operator and then newline follows. This causes the line
string: abcd
to be printed to the screen. Because there is no newline character after the first
string, the next value is printed immediately afterwards. This is called concatenating
the two values.
7
On this line, are printed the string char: , the character a and a newline.
18
Simple Input/Output
8
To print a character is used the function char() and the ASCII cod character.
9 The value 27 is the cod for a non-printing character, the character escape.
10..12 On this lines are included different type numbers determined automatically by the
compiler
The effect of Listing 6 is Dialog 5.
Dialog 5
It will be printed different type of data:
string : abcd"
char : a
char : a
non-printing char (escape):
integer : 123
long : -1234567
double : 123.456
Reading input
The iostreams class provides the ability to read input. The object used for standard input is cin.
cin normally expects input from the console, but input can be redirected from other sources. (The
redirection isnt object of this chapter).
The iostreams operator used with cin is >>. This operator waits for the same kind of input as its
argument. For example, if you give it an integer argument, it waits for an integer from the
console. Here's an example program that reads an integer and a real and it prints it.
Listing 7
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream.h>
int main (void)
{
int integer;
float real ;
cout << "Enter a decimal number: ";
cin >> integer;
cout << "Enter a real number: ";
cin >> real;
cout << "value of integer is: " << integer << endl;
cout << "value of real is: " << real << endl;
}
Notice the declaration of the integer number at the beginning of main( ). Since the extern
keyword isn't used, the compiler creates space for number at that point.
Delia Ungureanu - UNITBV
19
Simple Input/Output
Note: Both << and >> return their left operand as their result, enabling multiple input
or multiple output operations to be combined into one statement. This is illustrated by
Listing 6, Listing 7
The output formatting available with iostreams includes number formatting in decimal, octal and
hex. Here's another example of the use of iostreams:
Listing 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream.h>
int main (void)
{
// Specifying formats with manipulators:
int integer;
cout << "input a number in decimal: " ;
cin >> integer;
cout << "in octal is: " << oct << integer << endl;
cout << "in hex is: " << hex << integer << endl;
cout << "input a number in octal: " ;
cin >> oct >> integer;
cout << "in decimal is: " << dec << integer << endl;
cout << "in hex is: " << hex << integer << endl;
}
This example shows the iostreams class printing numbers in decimal, octal and hexadecimal
using iostream manipulators (which don't print anything, but change the state of the output
stream).
Dialog Error! No text of specified style in document.6
1
2
3
4
5
6
Comments:
If at Listing 8 execution on first line you introduce decimal value 123, on next lines it
will be printed as octal value (173) and hexadecimal value (7b).
On the line number 4 you must introduce an octal value, so all digits must be among 0 ..
7. On line 5 the value will be printed as decimal value, 83 and on line 6 as hexadecimal
value, 53.
20
Variables
A variable is a named data storage location in your computer's memory. By using a variable's
name in your program, you are, in effect, referring to the data stored there.
All variables have two important attributes (we will discuss about later in this chapter):
A type which is established when the variable is defined (e.g., integer, real,
character). Once defined, the type of a C++ variable cannot be changed.
A value which can be changed by assigning a new value to the variable. The kind
of values a variable can assume depends on its type. For example, an integer
variable can only take integer values (e.g., 1, 99, -555).
Variable Names
Your variable's name (for example, myVariable) is an identifier. Keywords can't be used as
variable names.
The following list contains some examples of legal and illegal C variable names:
number
// legal
Percent
// legal
annual_profit
// legal
_1111_2222
circle*radius
float
Note: C/C++ programmers commonly use only lowercase letters in variable names,
although this isn't required. Using all-uppercase letters is usually reserved for the
names of constants (which are covered later in this chapter).
DO use variable names that are descriptive.
DO adopt and stick with a style for naming your variables.
DON'T start your variable names with an underscore unnecessarily.
DON'T name your variables with all capital letters unnecessarily.
21
Variables
ease with which certain mathematical operations can be performed on them. Small integers (for
example, 1, -5, and 125) require less memory to store, and your computer can perform
mathematical operations (addition, multiplication, and so on) with such numbers very quickly. In
contrast, large integers and floating-point values (123456789 or 0.123456789, for example)
require more storage space and more time for mathematical operations. By using the appropriate
variable types, you ensure that your program runs as efficiently as possible.
There are two numerical categories:
Integer variables that have no fractional part. Integer variables come in two flavors:
signed integer variables can hold positive or negative values, whereas unsigned integer
variables can hold only positive values (and 0).
Floating-point variables hold values that have a fractional part (that is, real numbers).
Within each of these categories are more specific variable types. These are summarized in Table
2, which also shows the amount of memory, in bytes, required to hold a single variable of each
type.
Table 2 Numeric data types.
Variable Type
Keyword
Character
char
Integer
int
Short integer
short
Long integer
long
Unsigned character
unsigned char
Unsigned integer
unsigned int
Unsigned short integer
unsigned short
Unsigned long integer
unsigned long
Single-precision floating-point
float
Double-precision floating-point
double
Long double-precision floatinglong double
point
1
Approximate range; precision = 7 digits.
2
Approximate range; precision = 15 digits.
3
Approximate range; precision = 19 digits.
Bytes
Required
1
2
2
4
1
2
2
4
4
8
10
Range
-128 to 127
-32768 to 32767
-32768 to 32767
-2,147,483,648 to 2,147,438,647
0 to 255
0 to 65535
0 to 65535
0 to 4,294,967,295
+/-(3.4E-383.4E+38)1
+/-(1.7E-3081.7E+308)2
+/-(3.4E-49321.1E4932)3
Note: The sizes of variables might be different from those shown in Error!
Reference source not found., depending on the compiler and the computer you are
using. You should consult your compiler's manual for the values that your variable
types can hold.
The C++ compiler generates executable code which maps data entities to memory locations. For
example, the variable definition:
Delia Ungureanu - UNITBV
22
Variables
int myVariable = 12000;
causes the compiler to allocate two bytes to represent myVariable. The exact number of bytes
allocated and the method used for the binary representation of the integer depends on the specific
C++ implementation. The compiler uses the address of the first byte at which myVariable is
allocated to refer to it. The above assignment causes the value 12000 to be stored as a 2s
complement integer in the two bytes allocated (see Figure 3).
Figure 3 Representation of an integer in memory.
Listing 9 will help you determine the size of variables on your particular computer.
Listing 9 A program that displays the size of variable types.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream.h>
int main (void)
{
cout << "\nA char
cout << "\nAn int
cout << "\nA short
cout << "\nA long
cout << "\nAn unsigned
cout << "\nAn unsigned
cout << "\nAn unsigned
cout << "\nAn unsigned
cout << "\nA float
cout << "\nA double
return 0;
}
23
Variables
Dialog 7
A char
is 1 bytes
An int
is 2 bytes
A short
is 2 bytes
A long
is 4 bytes
An unsigned char
is 1 bytes
An unsigned int
is 2 bytes
is 4 bytes
A float
is 4 bytes
A double
is 8 bytes
Note: Use the sizeof operator to return the size, in bytes, of the given expression or
type. On your computer, the number of bytes presented might be different.
Integer Numbers
An integer variable may be defined to be of type short, int, or long.
short
age = 20;
int
max_no = 32000;
long
distance = 500000;
By default, an integer variable is assumed to be signed (i.e., have a signed representation so that it
can assume positive as well as negative values). However, an integer can be defined to be
unsigned by using the keyword unsigned in its definition. The keyword signed is also allowed but
is redundant.
unsigned short
age = 20;
unsigned int
max_no = 32000;
unsigned long
distance = 500000;
A literal integer (e.g., 2004) is always assumed to be of type int. If it has an L or l suffix it is
treated as a long. Also, a literal integer can be specified to be unsigned using the suffix U or u.
For example:
2004L
2004l
2004U
2004u
2004LU
2004ul
Literal integers can be expressed in decimal, octal, and hexadecimal notations. The decimal
notation is the one we have been using so far. An integer is taken to be octal if it is preceded by a
zero (0), and hexadecimal if it is preceded by a 0x or 0X. For example:
Delia Ungureanu - UNITBV
24
Variables
165
// decimal
0245
0xA5
Octal numbers use the base 8, and can therefore only use the digits 0-7.
Hexadecimal numbers use the base 16, and therefore use the letter A-F
Note:
Real Numbers
A real variable may be defined to be of type float, double or long double. On some PC, a float
uses 4, a double uses 8 bytes and a long double 10 bytes.
float
radius = 4.5;
double
pi = 3.141592654;
A literal real (e.g., 0.06) is always assumed to be of type double. If it has an F or f suffix it is
treated as a float, or an L or l suffix it is treated as a long double. For example:
0.01F
0.01f
3.1415L
3.1415l
Literal reals may also be expressed in scientific notation. For example, 0.001234 may be written
in the scientific notation as:
1.234E-3
or
1.234e-3
The letter E (or e) stands for exponent. The scientific notation is interpreted as follows:
1.234E-3 = 1.234 10-3
Characters
A character variable is defined to be of type char. A character variable occupies a single byte
which contains the code for the character. This code is a numeric value and depends on the
character coding system being used (i.e., is machine-dependent). The most common system is
ASCII (American Standard Code for Information Interchange). For example, the character A has
the ASCII code 65, and the character a has the ASCII code 97.
char ch = 'A';
Like integers, a character variable may be specified to be signed or unsigned. By the default (on
most systems) char means signed char. However, on some systems it may mean unsigned char. A
signed character variable can hold numeric values in the range -128 through 127. An unsigned
character variable can hold numeric values in the range 0 through 255. As a result, both are often
Delia Ungureanu - UNITBV
25
Variables
used to represent small integers in programs (and can be assigned numeric values like integers):
signed char
offset = -88;
unsigned char
A literal character is written by enclosing the character between a pair of single quotes (e.g.,
A).
Nonprintable characters are represented using escape sequences (see Table 3 Table 3).
Table 3 Escape sequences
Sequence
Character
Meaning
\a
alarm (bell)
alarm
\b
BS
backspace
\f
FF
formfeed
\n
LF
\r
CR
carrige return
\t
TAB
tab orizontal
\v
VT
tab vertical
\\
backslash (\)
\?
question mark
\o
any character
octal digits
\xH
any character
hexadecimal digits
Literal characters may also be specified using their numeric code value. The general escape
sequence \ooo (i.e., a backslash followed by up to three octal digits) is used for this purpose. For
example (assuming ASCII):
'\12'
'\11'
'\101'
'\0'
Strings
Text inside double quotes is called a string. The compiler creates space for strings and stores the
ASCII equivalent for each character in this space. The string is terminated with a value of 0 to
indicate the end of the string. A string is a consecutive sequence (i.e., array) of characters which
Delia Ungureanu - UNITBV
26
Variables
are terminated by a null character.
A string variable is defined to be of type char* (i.e., a pointer to character). A pointer is simply
the address of a memory location. (Pointers will be discussed later). A string variable contains the
address of where the first character of a string appears. For example, consider the definition:
char*str = "STRING";
Figure 4 illustrates how the string variable str and the string "STRING" might appear in memory.
Figure 4 A string variable in memory.
A literal string is written by enclosing its characters between a pair of double quotes (e.g.,
"STRING). The compiler always appends a null character to a literal string to mark its end. The
characters of a string may be specified using any of the notations for specifying literal characters.
For example,
"Name\tAddress\tTelephone"
// tab-separated words
A long string may extend beyond a single line, in which case each of the preceding lines should
be terminated by a backslash. For example:
"Example to show \
the use of backslash for \
writing a long string"
The backslash in this context means that the rest of the string is continued on the next line. The
above string is equivalent to the single line string:
"Example to show the use of backslash for writing a long string"
Note:
A common programming error results from confusing a single-character
string (e.g., "A") with a single character (e.g., A). These two are not equivalent. The
first consists of two bytes (the character A followed by the character \0), whereas
the latter consists of a single byte.
Note:
The shortest possible string is the null string ("") which simply consists of
the null character.
27
Variables
Symbolic Constants
A symbolic constant is a constant that is represented by a name (symbol) in your program. Like a
literal constant, a symbolic constant can't change. Whenever you need the constant's value in your
program, you use its name as you would use a variable name. The actual value of the symbolic
constant needs to be entered only once, when it is first defined.
Symbolic constants have two significant advantages over literal constants, as the following
example shows. Suppose that you're writing a program that performs a variety of geometrical
calculations. The program frequently needs the value 3.14159 for its calculations. (You might
recall from geometry class that, is the ratio of a circle's circumference to its diameter.) For
example, to calculate the circumference and area of a circle with a known radius, you could write
circumference = 3.14159 * (2 * radius);
area = 3.14159 * (radius)*(radius);
The asterisk (*) is the multiplication operator and is covered later in this chapter. Thus, the first
of these statements means "Multiply 2 times the value stored in the variable radius, and then
multiply the result by 3.14159. Finally, assign the result to the variable named circumference."
If, however, you define a symbolic constant with the name PI and the value 3.14, you could write
circumference = PI * (2 * radius);
area = PI * (radius)*(radius);
The resulting code is clearer. Rather than puzzling over what the value 3.14 is for, you can see
immediately that the constant PI is being used.
The second advantage of symbolic constants becomes apparent when you need to change a
constant. Continuing with the preceding example, you might decide that for greater accuracy your
program needs to use a value of PI with more decimal places: 3.14159 rather than 3.14. If you
had used literal constants for PI, you would have to go through your source code and change each
occurrence of the value from 3.14 to 3.14159. With a symbolic constant, you need to make a
change only in the place where the constant is defined.
C++ has two methods for defining a symbolic constant: the #define directive and the const
keyword.
This creates a constant named CONSTNAME with the value of literal. literal represents a literal
constant, as described earlier. CONSTNAME follows the same rules described earlier for
identifiers.
Note:
By convention, the names of symbolic constants are uppercase; this makes
them easy to distinguish from variable names, which by convention are lowercase.
For the previous example, the required #define directive would be
Delia Ungureanu - UNITBV
28
Variables
#define PI 3.14159
Note:
Note:
#defines can be placed anywhere in your source code, but they are in effect
only for the portions of the source code that follow the #define directive. Most
commonly, programmers group all #defines together, near the beginning of the file
and before the start of main().
const affects all variables on the declaration line. In the last line, min and max are symbolic
constants.
Note:
If your program tries to modify a const variable, the compiler generates an
error message, as shown here:
const int min = 100;
min = 1000;
/* Does not compile! Cannot reassign or alter
the value of a constant. */
Delia Ungureanu - UNITBV
29
Variables
Listing 10 A program that demonstrates the use of variables and constants.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Comments:
This program declares the two types of symbolic constants in lines 6 and 9. In line
6, a constant is used to make the value 454 more understandable. Because it uses
GRAMS_PER_POUND, line 22 is easy to understand. Lines 14 and 15 declare the
variables used in the program. Notice the use of descriptive names such as
weight_in_grams. You can tell what this variable is used for. Lines 22 and 23
calculate the weight in grams and in kilograms. These statements are covered
below in this chapter. To finish the program, lines 26 and 27 display the results.
If when you run Listing 10 you introduce 100 as value for weight_in_pound, on the screen will
display:
30
Variables
Dialog 8
Enter the weight in pounds: 100
The weight in grams = 45400
The weight in kilos = 45.4
31
An expression is any computation which yields a value. When discussing expressions, we often
use the term evaluation. For example, we say that an expression evaluates to a certain value. In
some cases, the expression may also produce side-effects. These are permanent changes in the
program state. In this sense, C++ expressions are different from mathematical expressions.
C++ provides operators for composing arithmetic, relational, logical, bitwise, and conditional
expressions. It also provides operators which produce useful side-effects, such as assignment,
increment, and decrement. We will also discuss the precedence rules which govern the order of
operator evaluation in a multi-operator expression.
An operator is a symbol that instructs C++ to perform some operation, or action, on one or more
operands. An operand is something that an operator acts on. In C++, all operands are
expressions. C++ operators fall into several categories according to number of operand (unary,
binary and ternary operators) or type of actions (arithmetic, relational, logical, bitwise, etc.).
When executed, expression is evaluated, and the resulting value is assigned to variable.
If you write:
x = y;
assigns the value that is the result of adding a and b to the operand x.
An operand that legally can be on the left side of an assignment operator is called an lvalue. That
which can be on the right side is called an rvalue.
An lvalue (standing for left value) is anything that denotes a memory location in which a value
may be stored. The only kind of lvalue we have seen so far in this book is a variable. Other kinds
of lvalues (based on pointers and references) will be described later in this book.
32
// ok
Note:
An lvalue is an operand that can be on the left side of an expression. An
rvalue is an operand that can be on the right side of an expression. Note that all lvalues are r-values, but not all r-values are l-values. An example of an rvalue that is
not an lvalue is a literal. Thus, you can write x = 5;, but you cannot write 5 = x;.
Arithmetic Operators
C++'s arithmetic operators perform mathematical operations. C++ has four unary arithmetic
operators and five binary arithmetic operators.
Symbol
Action
Examples
Plus
+x
Minus
-x
Increment
++
++x, x++
Decrement
--
--x, x--
The increment and decrement operators can be used only with variables, not with constants. The
operation performed is to add one to or subtract one from the operand. In other words, the
statements
++x;
--y;
The increment and decrement operators can be placed before its operand (prefix mode) or after its
Delia Ungureanu - UNITBV
33
when use in prefix mode, the increment and decrement operators modify their operand
before it's used.
when use in postfix mode, the increment and decrement operators modify their
operand after it's used.
After these statements are executed, x has the value 11, and y has the value 10. The value of x
was assigned to y, and then x was incremented. In contrast, the following statements result in
both y and x having the value 11. x is incremented, and then its value is assigned to y.
x = 10;
y = ++x;
Listing 11 illustrates the difference between prefix mode and postfix mode.
Listing 11 Demonstrates prefix and postfix modes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=
=
=
=
=
"
"
"
"
"
<<
<<
<<
<<
<<
a-a-a-a-a--
<<
<<
<<
<<
<<
",
",
",
",
",
b
b
b
b
b
=
=
=
=
=
"
"
"
"
"
<<
<<
<<
<<
<<
--b;
--b;
--b;
--b;
--b;
Comments:
This program declares two variables, a and b, in line 6. In line 8, the variables are
set to the value of 5. With the execution of each output statement (lines 13 through
17), both a and b are decremented by 1. After a is printed, it is decremented,
whereas b is decremented before it is printed.
Delia Ungureanu - UNITBV
34
=
=
=
=
=
5,
4,
3,
2,
1,
b
b
b
b
b
=
=
=
=
=
4
3
2
1
0
Note:
The increment and decrement operators can only be applied to variables;
an expressions like
x=(i+j)++;
5++;
are illegal.
Symbol
Action
Examples
Addition
x+y
Subtraction
x- y
Multiplication
x* y
Division
x/y
Modulus
Except for remainder (%) all other arithmetic operators can accept a mix of integer and real
operands. Generally, if both operands are integers then the result will be an integer. However, if
one or both of the operands are reals then the result will be a real.
When both operands of the division operator (/) are integers then the division is performed as an
integer division and not the normal division we are used to. Integer division always results in an
integer outcome (i.e., the result is always rounded down). For example:
9 / 2
-9 / 2
35
distance = 100;
int
time = 80;
float
// gives 1.25
The remainder operator (%) expects integers for both of its operands. It returns the remainder of
integer-dividing the operands. For example 22 *5 is calculated by integer dividing 22 by 5 to give
an outcome of 4 and a remainder of 2; the result is therefore 2.
Note:
It is possible for the outcome of an arithmetic operation to be too large for
storing in a designated variable. This situation is called an overflow. The outcome of
an overflow is machine-dependent and therefore undefined. For example:
char
a = 20 *15;
Note:
It is illegal to divide a number by zero. This results in a run-time divisionby-zero failure which typically causes typically causes the program to terminate.
Bitwise Operators
C++ provides six bitwise operators for manipulating the individual bits in an integer quantity.
These are summarized in Table 6.
Table 6 Bitwise operators.
Operator
Symbol
Action
Examples
Bitwise
Negation
Bitwise And
&
x&y
Bitwise Or
x| y
Bitwise
Exclusive Or
x^y
Bitwise
Shift
Left
<<
x << y
Bitwise Right
Shift
>>
x >> y
~x
Bitwise operators expect their operands to be integer quantities and treat them as bit sequences.
Bitwise negation is a unary operator which reverses the bits in its operands. Bitwise and
Delia Ungureanu - UNITBV
36
Value
Bit Sequence
23
~ x
232
23
45
x & y
23
45
x | y
63
23
45
x ^ y
58
23
x >> 2
23
x << 2
92
Relational Operators
C++ does not have a built-in boolean type. In C++, zero is considered false, and all other values
are considered true, although true is usually represented by 1. Thus, if an expression is false, it is
equal to zero, and if an expression is equal to zero, it is false. If a statement is true, all you know
Delia Ungureanu - UNITBV
37
The relational operators are used to determine whether two numbers are equal, or if one is greater
or less than the other. Every relational statement evaluates to either 1 (TRUE) or 0 (FALSE).
C++ provides six relational operators for comparing numeric quantities. These are summarized in
Table 8.
Table 8 Relational Operators
Operator
Symbol
Action
Examples
Equal
==
5 == 5 //gives 1
Inequal
!=
5 != 5 //gives 0
Less Than
<
5 < 5.5//gives 1
Less Than
Equal
Greater Than
or <=
>
5 > 5.5//gives 0
6.3>= 5//gives 1
The operands of a relational operator must evaluate to a number. Characters are valid operands
since they are represented by numeric values. For example (assuming ASCII coding):
'A' < 'F'
Note:
The relational operators should not be used for comparing strings, because
this will result in the string addresses being compared, not the string contents. For
example, the expression
"STRING1" < "STRING2"
causes the address of "STRING1" to be compared to the address of "STRING2". As
these addresses are determined by the compiler (in a machine-dependent manner), the
outcome may be 0 or may be 1, and is therefore undefined.
C++ provides library functions (e.g., strcmp) for the lexicographic comparison of
strings.
Note:
The <= and >= operators are only supported in the form shown. In
particular, =< and => are both invalid and do not mean anything.
DON'T confuse ==, the relational operator, with =, the assignment operator. This is
Delia Ungureanu - UNITBV
38
Logical Operators
C++ provides three logical operators for combining logical expression. These are summarized in
Table 9. Like the relational operators, logical operators evaluate to 1 or 0.
Table 9 Logical Operators
Operator
Symbol
Action
Examples
NOT
AND
&&
OR
||
5 < 6 || 6 < 5
// gives 1
Logical negation is a unary operator, which negates the logical value of its single operand. If its
operand is nonzero it produces 0, and if it is 0 it produces 1.
Logical and produces 0 if one or both of its operands evaluate to 0. Otherwise, it produces 1.
Logical or produces 0 if both of its operands evaluate to 0. Otherwise, it produces 1.
!20
// gives 0
10 && 5
// gives 1
10 || 5.5
// gives 1
0 || 3
// gives 1
10 && 0
// gives 0
Conditional Operator
The conditional operator is C++'s only ternary operator, meaning that it takes three operands. Its
syntax is
exp1 ? exp2 : exp3;
39
Note:
The conditional operator functions somewhat like an if statement. The
preceding statement could also be written like this:
if (x > y)
max = x;
else
max = y;
Note:
Note that of the second and the third operands of the conditional operator
only one is evaluated. This may be significant when one or both contain side-effects
(i.e., their evaluation causes a change to the value of a variable). For example, in
int max = (x > y ? x++ : y++);
only one of x and y is incremented.
Note:
Because a conditional operation is itself an expression, it may be used as
an operand of another conditional operation, that is, conditional expressions may be
nested. For example:
int x = 1, y = 2, z =3;
int max = (x >y ? (x > z ? x : z) : (z > y ? z : y));
Using a compound assignment operator, which you can think of as a shorthand method of
assignment, you would write
x += 5;
In more general notation, the compound assignment operators have the following syntax (where
op represents a binary operator):
exp1 op= exp2
40
These are summarized in Table 10. The examples assume that x and y are integer variables.
Table 10 Compound Assignment operators.
Operator
Example
Equivalent To
x = y
+=
x += y
x = x + y
-=
x -= y
x = x - y
*=
x *= y
x = x * y
/=
x /= y
x = x / y
%=
x %= y
x = x % y
&=
x &= y
x = x & y
|=
x |= y
x = x | y
^=
x ^= y
x = x ^ y
<<=
x <<= y
x = x << y
>>=
x >>= y
x = x >> y
Note:
An assignment operation is itself an expression whose value is the value
stored in its left operand. An assignment operation can therefore be used as the right
operand of another assignment operation. Any number of assignments can be
concatenated in this fashion to form one expression. For example:
int x, y, z;
x = y = z = 99;
// means: x = (y = (z = 99));
// means: x = x + (y = z = 99);
Comma Operator
The comma is frequently used in C++ as a simple punctuation mark, serving to separate variable
declarations, function arguments, and so on. In certain situations, the comma acts as an operator
rather than just as a separator. You can form an expression by separating two subexpressions with
a comma. The result is as follows:
Both expressions are evaluated, with the left expression being evaluated first.
For example:
int x, y, z;
int min = 100, max = 1000;
Delia Ungureanu - UNITBV
41
For example, the above statement assigns the value of b to x, then increments a, and then
increments b.
Parentheses Operator
Parentheses () are used for:
group expressions
For example:
long hours, minutes, seconds;
seconds = ( hours *60 + minutes ) *60;
For complex expressions, you might need to nest parentheses one within another. For example:
int a, b, c, d, e;
e = ((a+b)*(a-c))+d;
Operator Precedence
The order in which operators are evaluated in an expression is significant and is determined by
precedence rules. These rules divide the C++ operators into a number of precedence levels (see
Table 11). Operators in higher levels take precedence over operators in lower levels. When an
expression is evaluated, operators with higher precedence are performed first.
Table 11 Operator precedence levels and order
Priority
(highest)
Operator
Order
1 () [] - . ::
2 !
Left to Right
++
--
&
Priority
Operator
sizeof new delete
Order
3 .*
->*
Left to Right
4 *
/ %
Left to Right
5 + 6 <<
Left to Right
>>
Left to Right
>=
Left to Right
8 = = !=
Left to Right
9 &
Left to Right
10 ^
Left to Right
11 |
Left to Right
12 &&
Left to Right
13 ||
Left to Right
14 ?: (conditional operator)
Right to Left
15 = *= /= %=
<<= >>=
(lowerest) 16 , (comma)
+= -=
&=
^= |= Right to Left
Left to Right
For example, in
a == b + c * d
c * d is evaluated first because * has a higher precedence than + and ==. The result is then added
to b because + has a higher precedence than ==, and then == is evaluated. Precedence rules can
be overridden using brackets. For example, rewriting the above expression as
a == (b + c) * d
Type Conversion
A value in any of the built-in types we have see so far can be converted (type-cast) to any of the
other types. For example:
(int) 3.14
Delia Ungureanu - UNITBV
43
(double) 2
(char) 122
As shown by these examples, the built-in type identifiers can be used as type operators. Type
operators are unary (i.e., take one operand) and appear inside brackets to the left of their operand.
This is called explicit type conversion. When the type name is just one word, an alternate
notation may be used in which the brackets appear around the operand:
int(3.14)
In some cases, C++ also performs implicit type conversion. This happens when values of
different types are mixed in an expression. For example:
double
d = 1;
// d receives 1.0
int
i = 10.5;
// i receives 10
i = i + d;
// means: i = int(double(i) + d)
In the last example, i + d involves mismatching types, so i is first converted to double (promoted)
and then added to d. The result is a double which does not match the type of i on the left side of
the assignment, so it is converted to int (demoted) before being assigned to i.
The above rules represent some simple but common cases for type conversion. More complex
cases will be examined later in other chapters.
44
Sta tements
At its heart, a program is a set of commands executed in sequence. The power in a program
comes from its capability to execute one or another set of commands, based on whether a
particular condition is true or false. This chapter introduces the various forms of C++ statements
for composing programs, so you will learn about
Expressions
Composed instructions
Decision instructions
Loop instructions
A statement is a complete direction instructing the computer to carry out some task. Statements
represent the lowest-level building blocks of a program. Each statement represents a
computational step which has a certain side-effect. (A side-effect can be thought of as a change
in the program state, such as the value of a variable changing because of an assignment.)
Statements enable the program to serve a specific purpose (e.g., sort a list of names).
In C++, statements are usually written one per line, although some statements span multiple lines.
C++ statements always end with a semicolon (except for preprocessor directives such as #define
and #include).
Like many other procedural languages, C++ provides different forms of statements for different
purposes. Declaration statements are used for defining variables. Assignment-like statements are
used for simple, arithmetical computations. Branching statements are used for specifying
alternate paths of execution, depending on the outcome of a logical condition. Loop statements
are used for specifying computations which need to be repeated until a certain logical condition is
satisfied. Flow control statements are used to divert the execution path to another part of the
program. We will discuss these in turn.
For example,
int a, b=5;
a=b+12 ;
++a;
a + 5;
// useless statement!
The last example represents a useless statement, because it has no side-effect (a is added to 5 and
the result is just discarded).
45
Statements
or as
a = b + 12 ;
or as
a =
b +
12;
Although this last variation is perfectly legal but is not recommended. Whitespace can be used to
make your programs more readable and easier to maintain.
Whitespace characters (spaces, tabs, and newlines) cannot be seen. If these characters
are printed, you see only the white of the paper.
DO use whitespace judiciously to make your code clearer.
Null Statements
The simplest statement is the null statement which consists of just a semicolon:
;
// null statement
Although the null statement has no side-effect, we will see that it is necessary sometimes.
Compound Statements
A compound statement, also called a block, is a group of two or more C++ statements enclosed in
braces. Here's an example of a block:
{
cout << HELLO WORLD !;
cout << \n;
cout << HELLO WORLD, AGAIN !;
}
In C++, a block can be used anywhere a single statement can be used. The enclosing braces can
be positioned in different ways but it's a good idea to place braces on their own lines, making the
beginning and end of blocks clearly visible. Placing braces on their own lines also makes it easier
to see whether you've left one out.
Delia Ungureanu - UNITBV
46
Statements
DO put block braces on their own lines. This makes the code easier to read.
DO line up block braces so that it's easy to find the beginning and end of a block.
DON'T spread a single statement across multiple lines if there's no need to do so.
Limit statements to one line if possible.
Compound statements are useful in two ways:
they allow us to put multiple statements in places where otherwise only single statements
are allowed
Comments:
For example, the scope of a and b is whole main() function. The scope of c is from
where they are defined till the closing brace of the compound statement. Outside the
compound statement, these variables are not defined, so the line 16 will produces an
error message like Undefined symbol 'c' in function main(). Blocks and scope rules
will be described in more detail when we discuss functions in the next chapter.
47
Statements
The if Statement
Relational operators are used mainly to construct the relational expressions used in if (conditional
statement) and repetitive statements (such while, do..while, for), covered in detail below in this
chapter.
You might be wondering what a program control statement is. Statements in a C program
normally execute from top to bottom, in the same order as they appear in your source code file. A
program control statement modifies the order of statement execution. Program control statements
can cause other program statements to execute multiple times or to not execute at all, depending
on the circumstances.
The if statement is one of C++'s program control statements.
The form of an if statement is as follows:
if (expression)
statement;
DO indent statements within a block to make them easier to read. This includes the
statements within a block in an if statement.
DON'T make the mistake of putting a semicolon at the end of an if statement. An if
statement should end with the conditional statement that follows it.
In the following, statement1 executes whether or not x equals 2, because each line is evaluated as
a separate statement, not together as intended:
if( x == 2);
statement1;
48
Statements
Listing 13 Using if statements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Comments:
Listing 13 shows three if statements in action (lines 13 through 18).
Line 6 declares two variables, x and y, and lines 9 through 11 prompt the user for
values to be placed into these variables. Lines 13 through 18 use if statements to
determine whether x is greater than, less than, or equal to y.
Line 13 uses an if statement to see whether x is equal to y. Remember that ==, the
equal operator, means "is equal to" and should not be confused with =, the
assignment operator. After the program checks to see whether the variables are equal,
in line 15 it checks to see whether x is greater than y, followed by a check in line 17
to see whether x is less than y.
Note: You will notice that the statements within an if clause are indented. This is a
common practice to aid readability.
49
Statements
statement1;
else
statement2;
Comments:
Lines 13 through 19 are slightly different from the previous listing (Listing 14). Line
13 still checks to see whether x equals y. If x does equal y, x is equal to y appears onscreen, just as in Listing 14. However, the program then ends, and lines 16 through 19
aren't executed. Line 16 is executed only if x is not equal to y , or, to be more
accurate, if the expression "x equals y" is false. If x does not equal y, line 16 checks
to see whether x is greater than y. If so, line 17 prints x is greater than y; otherwise
(else), line 19 is executed.
Listing 14 uses a nested if statement. Nesting means to place (nest) one or more C++ statements
inside another C++ statement. In the case of Listing 14, an if statement is part of the first if
statement's else clause.
A frequently-used form of nested if statements involves the else part consisting of another if-else
statement. For example:
Delia Ungureanu - UNITBV
50
Statements
Listing 15 Using nested if else statements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Comments:
Listing 15 demonstrates the use of nested if else statements. Line 8 read a character
to be placed into ch variable. On lines 9 to 18 is checked if ch is a digit, an upper
letter, a lower letter or is a special character.
The conditionals expressions used are complexes; they include relational and logical
operators.
case constant1:
statements;
...
case constantn:
statements;
Delia Ungureanu - UNITBV
51
Statements
default:
statements;
}
First expression (called the switch tag) is evaluated, and the outcome is compared to each of the
numeric constants (called case labels), in the order they appear, until a match is found. The
statements following the matching case are then executed. Note the plural: each case may be
followed by zero or more statements (not just one statement). Execution continues until either a
break statement is encountered or all intervening statements until the end of the switch statement
are executed. The final default case is optional and is exercised if none of the earlier cases
provide a match.
For example, suppose we have parsed a binary arithmetic operation into its three components and
stored these in variables x, y, and op. The following switch statement performs the operation and
stored the result in result.
Listing 16 Using switch statments
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
52
Statements
Comments:
As illustrated by Listing 16, it is usually necessary to include a break statement at the
end of each case. The break terminates the switch statement by jumping to the very
end of it. There are, however, situations in which it makes sense to have a case
without a break. For example, we * or x to be used as a multiplication operator, so in
line 17 isnt necessary break. Because case 'x' has no break statement (in fact no
statement at all!), when this case is satisfied, execution proceeds to the statements of
the next case and the multiplication is performed.
It should be obvious that any switch statement can also be written as multiple if-else
statements.
The above statement, for example, may be written as:
if (op == '+')
result = x + y;
else if (op == '-')
result = x - y;
else if (op == 'x' || op == '*')
result = x * y;
else if (op == '/')
result = x / y;
else
cout << "unknown operator: " << op << '\n';
In general, preference should be given to the switch version when possible. The if-else approach
should be reserved for situation where a switch cannot do the job (e.g., when the conditions
involved are not simple equality expressions, or when the case labels are not numeric constants).
Unlike embedded if-else logic, the switch statement doesn't need a lot of fancy indentation that
causes code to move closer to the right margin as the statement gets longer.
DO use the switch statement to code multiple-choice logic. You will improve your
program's clarity and make future maintenance much easier.
53
Statements
initial, condition, and increment are all C++ expressions, and statement is a single or compound
C++ statement. When a for statement is encountered during program execution, the following
events occur:
1. The expression initial is evaluated. initial is usually an assignment statement that sets a
variable to a particular value.
2. The expression condition is evaluated. condition is typically a relational expression.
3. If condition evaluates to false (that is, as zero), the for statement terminates, and
execution passes to the first statement following statement.
4. If condition evaluates to true (that is, as nonzero), the C statement(s) in statement are
executed.
5. The expression increment is evaluated, and execution returns to step 2.
Note:
Note that statement never executes if condition is false the first time it's
evaluated.
Listing 17 uses a for statement to print the numbers 1 through 20.
Listing 17 Using for loop statements
1
2
3
4
5
6
7
8
9
10
11
12
Comments:
Line 5 declares a type int variable, named count, that will be used in the for loop.
Lines 9 and 10 are the for loop. When the for statement is reached, the initial
Delia Ungureanu - UNITBV
54
Statements
statement is executed first. In this listing, the initial statement is count = 1. This
initializes count so that it can be used by the rest of the loop. The second step in
executing this for statement is the evaluation of the condition count <= 20. Because
count was just initialized to 1, you know that it is less than 20, so the statement in the
for command, the cout<<, is executed. After executing the printing function, the
increment expression, count++, is evaluated. This adds 1 to count, making it 2. Now
the program loops back and checks the condition again. If it is true, the cout <<
reexecutes, the increment adds to count (making it 3), and the condition is checked.
This loop continues until the condition evaluates to false, at which point the program
exits the loop and continues to the next line (line 11), which returns 0 before ending
the program.
The for statement is frequently used, as in the previous example, to "count up," incrementing a
counter from one value to another. You also can use it to "count down," decrementing (rather
than incrementing) the counter variable.
for (count = 100; count > 0; count--)
You can also "count by" a value other than 1, as in this example:
for (count = 0; count < 1000; count += 5)
The for statement is quite flexible. For example, you can omit the initialization expression if the
test variable has been initialized previously in your program. (You still must use the semicolon
separator as shown, however.)
count = 1;
for ( ; count < 1000; count++)
The initialization expression doesn't need to be an actual initialization; it can be any valid C++
expression. Whatever it is, it is executed once when the for statement is first reached. For
example, the following prints the statement Now begin for...:
count = 1;
for (cout<<Now begin for ; count < 1000; count++)
You can also omit the increment expression, performing the updating in the body of the for
statement. Again, the semicolon must be included. To print the numbers from 0 to 99, for
example, you could write
for (count = 0; count < 100; )
cout << count++;
The test expression that terminates the loop can be any C++ expression. As long as it evaluates to
true (nonzero), the for statement continues to execute. You can use C++'s logical operators to
construct complex test expressions. For example, the following for statement prints only lower
letters:
char ch;
cin >> ch;
for (; ch >=a && ch <=z; ch++)
cout << ch << endl;
Delia Ungureanu - UNITBV
55
Statements
Comma operator is most often used in for statements. You can create an expression by separating
two subexpressions with the comma operator. The two subexpressions are evaluated (in left-toright order). By using the comma operator, you can make each part of a for statement perform
multiple duties.
for (i = 0, j = 100; i < j; i++, j--)
cout << i << \t << j << \n;
The comma operator is used to initialize two variables, i and j. It is also used to increment part of
these two variables with each loop.
Comments:
When you run this program, if you read 5 for number of rows and 10 for number of
columns, * is printed on-screen by 50 times, forming a 5*10 rectangle. The program
has only one command to print an *, but it is nested in two loops.
Line 11 starts the first for loop. Looking at the condition, you see that this for loop is
executed until the i is 0. On first executing line 17, i is 5; therefore, the program
continues to line 13.
Line 13 contains the second for statement. The value of j is 10 initially (the value
Delia Ungureanu - UNITBV
56
Statements
passed via cols). Because j is greater than 0, line 14 is executed, printing an *. j is
then decremented, and the loop continues. When j is 0, the for loop ends, and control
goes to line 22. Line 15 causes the on-screen printing to start on a new line. After
moving to a new line on the screen, control reaches the end of the first for loop's
statements, thus executing the increment expression, which subtracts 1 from i,
making it 4. This puts control back at line 13 and actions are repeated until i become
0.
statement;
condition is any C++ expression, and statement (called the loop body) is a single or compound
C++ statement. When program execution reaches a while statement, the following events occur:
1. The expression condition is evaluated.
2. If condition evaluates to false (that is, zero), the while statement terminates, and
execution passes to the first statement following statement.
3. If condition evaluates to true (that is, nonzero), the C statement(s) in statement are
executed.
4. Execution returns to step 1.
Listing 19 is a simple program that uses a while statement to print the numbers 1 through 20.
(This is the same task that is performed by a for statement in Listing 17)
Listing 19 A simple while statement.
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(void)
{
int count;
/* Print the numbers 1 through 20 */
count = 1;
while(count <= 20)
{
cout << count << endl;
count++;
}
return 0;
}
Comments:
Examine Listing 19 and compare it with Listing 17, which uses a for statement to
Delia Ungureanu - UNITBV
57
Statements
perform the same task. In line 6, count is initialized to 1. Because the while statement
doesn't contain an initialization section, you must take care of initializing any
variables before starting the while. Line 7 is the actual while statement, and it
contains the same condition statement from Listing 17, count <= 20. In the while
loop, line 10 takes care of incrementing count. If you forgot to put line 10 in this
program, your program wouldn't know when to stop, because count would always be
1, which is always less than 20.
A while statement is essentially a for statement without the initialization and increment
components. Thus,
for ( ; condition ; )
is equivalent to
while (condition)
Note:
Anything that can be done with a for statement can also be done with a
while statement. When you use a while statement, any necessary initialization must
first be performed in a separate statement, and the updating must be performed by a
statement that is part of the while loop.
statement
while (condition);
condition is any C expression, and statement is a single or compound C statement. When program
execution reaches a do...while statement, the following events occur:
1. The statements in statement are executed.
2. condition is evaluated. If it's true, execution returns to step 1. If it's false, the loop
terminates.
Listing 20 is the same simple program that print the numbers 1 through 20, but it use do..while
statement instead while.
Delia Ungureanu - UNITBV
58
Statements
int main(void)
{
int count;
/* Print the numbers 1 through 20 */
count = 1;
do
{
cout << count << endl;
count++;
}
while(count <= 20);
return 0;
}
Comments:
While statement was changed with do..while. The same condition is evaluated, but at
the end, on line 12.
The statements associated with a do...while loop are always executed at least once. This is
because the test condition is evaluated at the end, instead of the beginning, of the loop. In
contrast, for loops and while loops evaluate the test condition at the start of the loop, so the
associated statements are not executed at all if the test condition is initially false.
The do...while loop is used less frequently than while and for loops. It is most appropriate when
the statement(s) associated with the loop must be executed at least once. You could, of course,
accomplish the same thing with a while loop by making sure that the test condition is true when
execution first reaches the loop. A do...while loop probably would be more straightforward,
however.
The do loop is less frequently used than the while loop. It is useful for situations where we need
the loop body to be executed at least once, regardless of the loop condition. For example, suppose
we wish to read a value non zero to calculate a division. This can be expressed as the following
loop:
float a, b;
cin >> a;
do {
cin >> b;
} while (b == 0);
Delia Ungureanu - UNITBV
59
Statements
cout << a/b;
{
// process num here...
}
When the continue statement appears inside nested loops, it applies to the loop immediately
enclosing it, and not to the outer loops. For example, in the following set of nested loops, the
continue applies to the for loop, and not the while loop:
while (more) {
for (i = 0; i < n; ++i) {
cin >> num;
if (num < 0) continue;
60
Statements
For example, suppose we wish to read in a user password, but would like to allow the user a
limited number of attempts:
for (i = 0; i < attempts; ++i) {
cout << "Please enter your password: ";
cin >> password;
if (Verify(password))
break;
Here we have assumed that there is a function called Verify which checks a password and
returns true if it is correct and false otherwise.
Rewriting the loop without a break statement is always possible by using an additional logical
variable ( verified) and adding it to the loop condition:
verified = 0;
for (i = 0; i < attempts && !verified; ++i) {
cout << "Please enter your password: ";
cin >> password;
verified = Verify(password));
if (!verified)
cout << "Incorrect!\n";
}
where expression denotes the value returned by the function. The type of this value should match
the return type of the function. For a function whose return type is void, expression should be
empty:
return;
The only function we have discussed so far is main, whose return type is always int. The return
value of main is what the program returns to the operating system when it completes its
execution. Under UNIX, for example, it its conventional to return 0 from main when the program
executes without errors. Otherwise, a non-zero error code is returned.
When a function has a non-void return value (as in the above example), failing to return a value
will result in a compiler warning. The actual return value will be undefined in this case (i.e., it
will be whatever value which happens to be in its corresponding memory location at the time).
Delia Ungureanu - UNITBV
61
Po inters
One of the most powerful tools available to a C++ programmer is the ability to manipulate
computer memory directly by using pointers.
When you declare a variable in a C++ program, the compiler sets aside a memory location with a
unique address to store that variable (see Figure 3). The compiler associates that address with the
variable's name. When your program uses the variable name, it automatically accesses the proper
memory location. The location's address is used, but it is hidden from you, and you need not be
concerned with it. If you want this information, though, you can use the address of operator (&),
which is illustrated in Listing 21.
Listing 21 Using address operator.
1
2
3
4
5
6
7
8
9
10
11
Comments:
This listing is an example to obtain information about a variable.
On line 6 variable var is declared with initialisation.
The type specified (i.e., float) determines the number of bytes allocated for var. It can
be obtained using sizeof operator (see line 8).
To determine the address where var is allocated in memory you can use & operator.
Line 9 will print memory address of var in hexadecimal format.
The type and the address of variable both cant be change during the program.
Output of Listing 21 is Dialog 10. The address reported for var might be another on your system.
62
Pointers
Dialog 10
Value of variable var is: 3.5
Variable var occupies 4 bytes in memory
Variable var is allocated in memory at 0x37e7245dc address
One of the most powerful tools available to a C++ programmer is the ability to manipulate
computer memory directly by using pointers.
A pointer is a variable that holds a memory address.
A pointer declaration takes the following form:
typename *ptrname;
// pointer to an int
char * ptr2;
// pointer to a char
float * ptr3
// pointer to a float
we can write:
ptr1 = &var1;
The expression
*ptr1
dereferences ptr1 to get to what it points to, and is equivalent to var1. The expression returns
the contents of the location to which ptr1 points (i.e., value 7, see
Figure 5).
Figure 5 Example for pointer variable.
63
Pointers
/* using pointer */
#include <iostream.h>
int main(void)
{
int var1 = 7;
int * ptr1;
ptr1 = &var1;
cout << "Value of variable ptr1 is: " << ptr1 <<endl;
cout << "Variable ptr1 point to " << *ptr1 << " value"<< endl;
return 0;
}
Comments:
In this listing, two variables are declared. In line 6, var1 is declared as an int and
initialized to 7.
In line 7 is declared a pointer to a variable of type int. The name of pointer is ptr1.
In line 8, the pointer ptr1 is assigned the address of var1 using the address-of operator
(&).
Line 9 prints the value of ptr1.
Line 10 prints the value stored in the location pointed to by ptr1.
Dialog 11
Value of variable ptr1 is: 0x29072256
Variable ptr1 point to 7 value
Listing 22 prints on the screen Dialog 11 (it might be different on your system).
A pointer may be assigned the value 0 (called the NULL pointer). The null pointer doesnt point
to any address. The NULL pointer is used for initializing pointers.
DON'T use an uninitialized pointer. Results can be disastrous if you do.
In general, the type of a pointer must be matching the type of the data it is set to point to.
A pointer of type void*, however, will match any type. This is useful for defining pointers which
may point to data of different types, or whose type is originally unknown.
This example is legal:
Delia Ungureanu - UNITBV
64
Pointers
int var1;
float var2;
void * pvar;
pvar = &var1;
//...
pvar = &var2;
A pointer may be converted to another type using typecast operator. For example,
ptr2 = (char*) ptr1;
// illegal!
illegal
// ok
// illegal!
illegal
str2[0] = 'P';
// ok
// illegal!
illegal
str3[0] = 'C';
// illegal!
illegal
Pointer Arithmetic
In C++ one can add an integer quantity to or subtract an integer quantity from a pointer. This is
frequently used by programmers and is called pointer arithmetic. Pointer arithmetic is not the
same as integer arithmetic, because the outcome depends on the size of the object pointed to. For
example, see Listing 23.
Listing 23 Example of pointer arithmetic.
65
Pointers
1
2
3
4
5
6
7
8
9
10
11
12
/* pointer arithmetic*/
#include <iostream.h>
int var1 = 7, var2=100;
int main(void)
{
int * ptr;
ptr = &var1;
cout << "ptr point to " << *ptr << endl;
cout << "ptr+1 point to " << *(ptr+1) << endl;
return 0;
}
Comments:
ptr points to var1, ptr+1 advances by one int (i.e., two bytes) so that it points to the
second variable, var2. This situation is illustrated in Figure 6.
Figure 6 Ilustration of pointer arithmetic.
Listing 24
ptr point to 7
ptr +1 point to 100
The expression
ptr + 4 ;
66
Pointers
For example,
ptr--;
ptr-=2;
Other pointer arithmetic operation is called differencing, which refers to subtracting two pointers.
If you have two pointers of the same type, you can subtract them and find out how far apart they
are.
ptr1 - ptr2;
Pointer comparisons are valid only between pointers that point to the same array. Under these
circumstances, the relational operators ==, !=, >, <, >=, and <= work properly. Thus, if ptr1 and
ptr2 point to elements of the same array, the comparison
ptr1 < ptr2
Many arithmetic operations that can be performed with regular variables, such as multiplication
and division, don't make sense with pointers. The C++ compiler doesn't allow them. For example,
if ptr is a pointer, the statement
ptr *= 2;
This pointer isn't yet initialized, so it doesn't point to anything known. An uninitialized pointer
has some value; you just don't know what it is. In many cases, it is zero. If you use an
uninitialized pointer in an assignment statement, this is what happens:
*ptr = 100;
The value 100 is assigned to whatever address ptr points to. That address can be anywhere in
memory: where the operating system is stored, where is the program's code, etc. The 100 might
overwrite some important information, and the result can be disastrous, even it might produce the
system crash.
67
Arrays
An array consists of a set of objects (called its elements), all of which are of the same type and
are arranged contiguously in memory. In general, only the array itself has a symbolic name, not
its elements. Each element is identified by an index which denotes the position of the element in
the array. The number of elements in an array is called its dimension. The dimension of an array
is fixed and predetermined; it cannot be changed during program execution.
Arrays are suitable for representing composite data which consist of many similar, individual
items. Some examples: a list of names, a series of numbers, etc.
The name of an array is a pointer, it contain the address of memory where the array is allocated.
It cant be modified, is a constant pointer.
Single-Dimensional Arrays
A single-dimensional array has only a single subscript. A subscript is a number in brackets that
follows an array's name. This number can identify the number of individual elements in the array.
Declaration of a single-dimensional array is:
type_el array_name [dim];
where array_name is the name of array that contains dim elements, each element type is type_el.
For example,
float temperatures[24];
The array is named temperatures, and it contains 24 elements. Each of the 24 elements is the
equivalent of a single float variable.
Note:
Note:
Individual elements of the array are accessed by using the array name followed by the element
subscript enclosed in square brackets. For example, the following statements store the value 3.5
in the first array element, 5.7 in the second array element and in the third holds the average sum
of the first and the second elements of array.
temperatures[0]=3.5;
temperatures[1]=5.7;
temperatures[2]=(temperatures[0]+temperatures[1])/2;
68
Arrays
You also can initialize the array at the same time you define it:
int nums[6]={3, 5, -7, 15, 99, 43};
Listing 25
1
2
3
4
5
6
7
8
9
10
11
12
13
Comments:
On 6 line array named nums is defined with initialisation.
On 7 line variable average is declared and it is initialised with 0. It is used to calculate
average sum of the 8 array elements.
To get every element of the array we use a for loop. Variable i is declared and is used
as array index.
In C++, characters array are used to create strings.
char name[] = Smith;
defines name to be an array of six characters: five letters and a null character. The terminating
Delia Ungureanu - UNITBV
69
Arrays
null character is inserted by the compiler.
Figure 8 Memory allocation for a string.
The elements of "SMITH" can be referred to as *name, *(name + 1), *(name + 2), etc.
Pointer arithmetic is very handy when processing the elements of an array. Listing 26 shows a
string copying function similar to strcpy.
Listing 26
1
2
3
4
5
Comments:
The condition of this loop assigns the contents of src to the contents of dest and then
increments both pointers. This condition becomes 0 when the final null character of
src is copied to dest.
Multi-Dimensional Arrays
A multidimensional array has more than one subscript. A two-dimensional array has two
subscripts, a three-dimensional array has three subscripts, and so on. There is no limit to the
number of dimensions a C++ array can have.
For example,
int matrix[3][4] = { {
70
Arrays
Figure 9 Organization of matrix in memory.
Multidimensional arrays are interpreted as arrays whose elements are of type array. matrix is an
array with 3 elements and every element is a four elements array.
matrix is the address of entire array, matrix[0] is the address of first row and matrix[0][0]
represents the value of first element on the first row.
Listing 27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Comments:
On line 6 is declared an tow-dimensional array with dimensions 3 x 4; its elements
are initialized.
To processing the array are used variables row and col and two nested for loops.
row gets successive values: 0, 1, 2 and col gets successive values: 0, 1, 2, 3.
Delia Ungureanu - UNITBV
71
Arrays
The result of running is the Dialog 12Dialog 12 printed on the screen.
Dialog 12
matrix[0][0]=10
matrix[0][1]=20
matrix[0][2]=30
matrix[0][3]=40
matrix[1][0]=50
matrix[1][1]=60
matrix[1][2]=70
matrix[1][3]=80
matrix[2][0]=90
matrix[2][1]=100
matrix[2][2]=110
matrix[2][3]=120
72
You can use this like any other pointer to a variable and assign a value into that area of memory
by writing
*ptr = 12.345;
To allocate a block large enough for storing an array of 10 characters, you can write:
char *str = new char[10];
If new cannot create memory on the free store (memory is, after all, a limited resource) it returns
the null pointer. You must check your pointer for null each time you request new memory.
Note: Each time you allocate memory using the new keyword, you must check to
make sure the pointer is not null.
The delete operator is used for releasing memory blocks allocated by new. It takes a pointer as
argument and releases the memory block to which it points. For example:
delete ptr;
// delete an object
delete [] str;
Note that when the block to be deleted is an array, an additional [] should be included to indicate
this.
Listing 28 Using dynamic memory.
1
2
3
4
73
Dynamic Memory
5
6
7
8
9
10
11
12
13
14
int main(void)
{
char *str1 = "learning about dynamic memory";
char *str2 = new char[strlen(str1) + 1];
strcpy(str2, str1);
cout << "contents of str2 is: " << str2;
delete []str2;
return 0;
}
Comments:
At line 3 is included the standard string header file which declares a variety of
functions for manipulating strings.
The strlen function (declared in string.h) counts the characters in its string
argument up to (but excluding) the final null character. Because the null character is
not included in the count, we add 1 to the total and allocate an array of characters of
that size.
The strcpy function (declared in string.h) copies its second argument to its first,
character by character, including the final null character.
str1 is a pointer that assigns address of constant string "learning about dynamic
memory".
str2 assigns the address of block allocated in heap using new operator. Dimension of
block depends on length of string allocated at str1 address, increased with one
because of string terminator (\0).
At line 12 is used delete operator to release the block allocated with new.
For every time in your program that you call new, there should be a call to delete. It
is important to keep track of which pointer owns an area of memory and to ensure that
the memory is returned to the free store when you are done with it.
74
References
A reference (&) is an alias for an object; when you create a reference, you initialize it with the
name of another object, the target. From that moment on, the reference acts as an alternative
name for the target, and anything you do to the reference is really done to the target.
A reference is like a constant pointer that is automatically dereferenced. It is usually used for
function argument lists and function return values. But you can also make a free-standing
reference. For example,
int x;
int& r = x;
defines r as a reference to x. After this definition x and r both refer to the same object, as if they
were the same variable. It should be emphasized that a reference does not create a copy of an
object, but merely a symbolic alias for it. Hence, after
x = 15;
num3 = num1;
You can also initialize a reference to a constant. In this case a copy of the constant is made (after
any necessary type conversion) and the reference is set to refer to the copy.
int &n = 1;
// n refers to a copy of 1
The reason that n becomes a reference to a copy of 1 rather than 1 itself is safety. Consider what
could happen if this were not the case.
int &x = 1;
++x;
int y = x + 1;
The most common use of references is for function parameters (we will be discussed in next
lecture).
Note: A reference must be initialized when it is created. (Pointers can be initialized at
any time.)
Note: Once a reference is initialized to an object, it cannot be changed to refer to
another object. (Pointers can be pointed to another object at any time.)
Note: You cannot have NULL references. You must always be able to assume that a
reference is connected to a legitimate piece of storage.
Delia Ungureanu - UNITBV
75
Functions
A function is, in effect, a subprogram that can act on data and return a value. Every C++ program
has at least one function, main(). When your program starts, main() is called automatically.
main() might call other functions, some of which might call still others.
A function is a named, independent section of C++ code that performs a specific task and
optionally returns a value to the calling program.
A function provides a convenient way of packaging a computational recipe, so that it can be used
as often as required.
A function definition consists of two aspects: prototype and proper definition.
The prototype specifies how it may be used. The function prototype is a statement, which means
it ends with a semicolon. It consists of the function's return type, name, and parameter list.
Entities of prototype:
The function name. This is simply a unique identifier.
The function parameters. This is a set of zero or more typed identifiers used for
passing values to and from the function. The parameter list is a list of all the
parameters and their types, separated by commas.
The function return type. This specifies the type of value the function returns. A
function which returns nothing should have the return type void.
For example, Figure 10 illustrates the parts of the function prototype. This prototype declares a
function named area() that returns a long and that has two parameters, both integers.
Figure 10 Function prototype.
The prototype
long area(int, int);
is perfectly legal, but adding parameter names makes your prototype clearer. The same function
with named parameters might be
long area(int length, int width);
The proper definition of a function consists of the function header and its body.
Delia Ungureanu - UNITBV
76
Functions
The header is exactly like the function prototype, except that the parameters must be named, and
there is no terminating semicolon.
The body of the function contains the computational steps (statements) that comprise the
function. The statements are enclosed in braces.
Function definition syntax is:
return_type function_name ( [type parameterName]...)
{
statements;
}
All statements within the body of the function must be terminated with semicolons, but the
function itself is not ended with a semicolon; it ends with a closing brace.
If the function returns a value, it should end with a return statement, although return
statements can legally appear anywhere in the body of the function.
Every function has a return type. If one is not explicitly designated, the return type will be int.
Be sure to give every function an explicit return type.
If a function does not return a value, its return type will be void.
Function definition is illustrated in Figure 11.
Figure 11 Function definition.
Using a function involves calling it. A function call consists of the function name followed by
the call operator parentheses (), inside which zero or more comma-separated arguments appear.
The number of arguments should match the number of function parameters. Each argument is an
expression whose type should match the type of the corresponding parameter in the function
prototype.
An example for the function area call is:
area ( 10, 20);
Delia Ungureanu - UNITBV
77
Functions
When a function call is executed, the arguments are first evaluated and their resulting values are
assigned to the corresponding parameters. The function body is then executed. Execution begins
with the first statement after the opening brace ({). Finally, the function return value (if any) is
passed to the caller.
Functions can also call other functions and can even call themselves (see the section "Recursion"
later in this chapter).
Since a call to a function whose return type is non-void yields a return value, the call is an
expression and may be used in other expressions. By contrast, a call to a function whose return
type is void is a statement.
Listing 29 shows the definition of a simple function which calculates the area of a rectangle.
Listing 29 A simple function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main(void)
{
int len, wid;
long Area;
cout << "Input the rectangle length: ";
cin >> len;
cout << "Input the rectangle width: ";
cin >> wid;
Area = area(len, wid);
// function area call
cout <<"Area = " << Area;
return 0;
}
// function area definition
long area( int length, int width)
{
long Area;
Area = length * width;
return (Area);
}
Comments:
The 4 line defines the function prototype. It consists in return type of the function
(long in this case), the function name area and the parameter list. Area has two
parameters which are both of type int.
Line 14 calls the function area and passes the variables input to it as the function's
arguments (the value of len is assigned to length and the value of wid is assigned to
Delia Ungureanu - UNITBV
78
Functions
width). The function's return value is assigned to the variable Area (area and Area
are two different identifiers).
Lines 19-24 define area function.
Line 19 is the header function (is similar to the function prototype).
On line 20 the brace marks the beginning of the function body.
Line 21 is a local variable definition.
Line 23 returns Area as the return value of the function.
The brace on line 24 marks the end of the function body.
Note that the syntax for parameters is similar to the syntax for defining variables: type identifier
followed by the parameter name. However, it is not possible to follow a type identifier with
multiple comma-separated parameters:
long area (int length, width)
// Wrong!
By value
By address
By reference
Passing by value is sometimes called passing by copy. When you pass an argument from one
function to another, the argument's value is passed to the receiving function, but the variable
itself isn't passed. The value parameter receives a copy of the value of the argument passed to it.
As a result, if the function makes any changes to the parameter, this will not affect the argument.
For example, in Listing 29 the two parameters are value parameters. When the function is called
and len passed to length, length receives a copy of the value of len. In some way, width receives a
copy of the value of wid.
The second way of passing data to a function is passing by address. Parameters used in this case
are pointers that hold addresses of arguments. When Visual C++ passes a variable by address, in
effect it passes the variable itself, which means that the receiving function can change the calling
function's variable. When the calling function regains control, any variable just passed by address
might be changed if the called function changed the argument.
The third way to pass data to a function is passing by reference. When you pass data by
reference, if the called function changes the data, C++ applies those same changes to the calling
function's data. The end result of passing by reference is identical to that of passing by address.
There is one exception, the syntax when you passing variables (nonarrays) by reference: if you
pass nonarrays by address you must precede the passed arguments with ampersands and also
precede all parameters in the called function with asterisks. When passing variables by reference,
you only have to precede the receiving parameters with ampersands.
For example,
Delia Ungureanu - UNITBV
79
Functions
void func1( int * x )
//
{ cout << x; }
//
Note: Be careful when passing by reference! Remember that any changes applied to
the receiving parameters will also apply to the sending function's arguments.
Within the context of function calls, the three styles of passing arguments are, respectively, called
pass-by-value, pass-by-address and pass-by-reference. It is perfectly valid for a function to use
pass-by-value for some of its parameters, pass-by-address for others and pass-by-reference for
others.
To observe the differences, consider the three swap functions in Listing 30.
Listing 30 Pass-by-Value, Pass-by-Address and Pass-by-Reference.
1
2
3
4
5
6
7
8
9
10
11
12
80
Functions
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Comments:
swap1 swaps x and y, this has no effect on the arguments passed to the function, because
swap1 receives a copy of the arguments. The change of copy does not affect the original.
swap2 use pointer parameters. By dereferencing the pointers, swap2 gets to the original
values and swaps them. The call syntax of swap2 demands passing the addresses of
variables a and b (see line 43).
swap3 use reference parameters. The parameters become aliases for the arguments passed
to the function and swap them. swap3 has the added advantage that its call syntax is the
same as swap1 and involves no addressing or dereferencing.
Delia Ungureanu - UNITBV
81
Functions
When you run the Listing 30, if you introduce 5 for a and 7 for b, it will produce the following
output (Dialog 13):
Dialog 13
a= 5
b= 7
Function swap1
Initial values: x=5, y=7
Final values: x=7, y=5
a=5, b=7
Function swap2
Initial values: *x=5, *y=7
Final values: *x=7, *y=5
a=7, b=5
Function swap3
Initial values: x=7, y=5
Final values: x=5, y=7
A=5, b=7
There is a significant difference in an array variable and a normal variable: An array variable is
really a pointer in disguise. So when you pass an array as a parameter, you are really passing its
address. This means that when you pass an array, you are effectively passing the contents of the
array by address. Therefore, when you pass an array, the contents of the array can also be
changed.
See for example, Listing 31.
Listing 31 Passing arrays to functions.
1
2
3
4
5
6
7
8
9
10
11
12
82
Functions
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Comments:
On line 5-12 and 14-18 are defined two function, reading_array and printing_array,
that have as parameters arrays.
In the main function is declared myArray with 10 elements of type int.
The name of an array is an address, so, when we call the two functions, we specify
the name myArray (line 24 and line 26).
The types of the elements of array parameters and array arguments must be the same.
In that case is int.
Symbolic Constants
A function parameter may also be declared to be constant. This may be used to indicate that the
function does not change the value of a parameter:
int func (const int par1, const int par2)
{
//...
}
The usual place for constant definition is within header files so that they can be shared by source
files.
83
Functions
// x is global
{
if (x > 0) {
double x;
//...
}
}
84
Functions
Scope Operator
Because a local scope overrides the global scope, having a local variable with the same name as a
global variable makes the latter inaccessible to the local scope.
Use the scope access (or resolution) operator ::(two semicolons) to access a global (or file
duration) name even if it is hidden by a local redeclaration of that name.
Listing 32 is an example of scope operator use.
Listing 32 Using scope operator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream.h>
int x=7;
// x is a global variable
void main(void)
{
int x = 9;
// x is local to main
cout << "x= " <<x <<endl;
// refers to local of main x
cout << "::x= " << ::x <<endl;
// refers to global x
{
int x = 11;
// x is local to bloc
cout << "x= " <<x <<endl;
// refers to local of bloc x
cout << "::x= " << ::x <<endl;
// refers to global x
}
}
Comments:
There are three x variable, a global variable x declared in line 3, a variable x with
function main scope declared in line 7 and a variable x declared in line 11 that is local
to nested bloc.
In lines 9 and 13 global variable is accessed using scope operator.
The result of running this listing is Dialog 14Dialog 14.
Dialog 14
x= 9
::x= 7
x= 11
::x= 7
85
Functions
Auto Variables
Use the auto modifer to define a local variable as having a local lifetime. That variable is also
called automatic.
Syntax of declaration of auto variables is:
[auto] <data-definition> ;
For example:
void func (void)
{
auto int x;
//...
}
This is rarely used because all local variables are by default automatic.
Register Variables
As mentioned earlier, variables generally denote memory locations where variable values are
stored. When the program code refers to a variable, the compiler generates machine code which
accesses the memory location denoted by the variable. For frequently-used variables efficiency
gains can be obtained by keeping the variable in a register instead thereby avoiding memory
access for that variable. Objects can be accessed notably faster when placed in a register.
Use the register storage class specifier to store the variable being declared in a CPU register (if
possible), to optimize access and reduce code. Syntax used is:
register <data definition> ;
For example,
register int i;
register point cursor;
register char* p;
Note: It is not possible to take the address of a name declared register, nor can a
register be global.
Note: register is only a hint to the compiler, and in some cases the compiler may
choose not to use a register when it is asked to do so. One reason for this is that any
machine has a limited number of registers and it may be the case that they are all in
use.
Even when the programmer does not use register declarations, many optimizing compilers try
to make an intelligent guess and use registers where they are likely to improve the performance
of the program.
Delia Ungureanu - UNITBV
86
Functions
The same argument may be applied to the global variables in this file that are for the private use
of the functions in the file. For example,
static int var;
A local variable in a function may also be defined as static. The variable will remain only
accessible within its local scope; however, its lifetime will no longer be confined to this scope,
but will instead be global. In other words, a static local variable is a global variable which is only
accessible within its local scope.
Like global variables, static local variables are automatically initialized to 0.
Static local variables are useful when we want the value of a local variable to persist across the
calls to the function in which it appears.
An example of using static variable is Listing 33 witch produces Dialog 15.
Listing 33 Using static variable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
87
Functions
Comments:
Variable m is local of function func. It is declared with static specifier an it is
initialized with 5 (implicit value is 0).
At first call of function func (line 15) m has value 5, and then it is increased, so it
becomes 6.
At second call m has initially value 6, and then it is changed to 7.
Dialog 15
x= 9
::x= 7
x= 11
::x= 7
// variable declaration
informs the compiler that i is actually defined somewhere (may be later in this file or in another
file). This is called a variable declaration (not definition) because it does not lead to any storage
being allocated for size.
It is not recommended to include an initializer for an extern variable, since this causes it to
become a variable definition and have storage allocated for it:
extern int size = 10;
// no longer a declaration!
If there is another definition for size elsewhere in the program, it will eventually produces an
error.
Function prototypes may also be declared as extern, but this has no effect when a prototype
appears at the global scope. It is more useful for declaring function prototypes inside a function.
For example:
double Tangent (double angle)
{
extern
double sin(double);
// defined elsewhere
extern
double cos(double);
// defined elsewhere
88
Functions
The best place for extern declarations is usually in header files so that they can
be easily included and shared by source files.
Runtime Stack
When you begin your program, your operating system (such as DOS or Microsoft Windows) sets
up various areas of memory based on the requirements of your compiler. As a C++ programmer,
you'll often be concerned with the global name space, the free store, the code space, and the
stack.
Global variables are in global name space.
The stack is a special area of memory allocated for your program to hold the data required by
each of the functions in your program. It is called a stack because it is a last-in, first-out queue.
Last-in, first-out means that whatever is added to the stack last will be the first thing taken off.
When data is "pushed" onto the stack, the stack grows; as data is "popped" off the stack, the stack
shrinks. It isn't possible to pop a dish off the stack without first popping off all the dishes placed
on after that dish.
C++ function call execution is based on a runtime stack. When a function is called, memory
space is allocated on this stack for the function parameters, return value, and local variables, as
well as a local stack area for expression evaluation. The allocated space is called a stack frame.
When a function returns, the allocated stack frame is released so that it can be reused.
For example, consider a situation where main calls a function called func1 which in turn calls
another function called func2:
int func2 (void)
{
//...
}
int func1 (void)
{
//...
func2();
//...
}
89
Functions
int main (void)
{
//...
func1();
//...
}
It is important to note that the calling of a function involves the overheads of creating a stack
frame for it and removing the stack frame when it returns.
Inline Functions
When you define a function, normally the compiler creates just one set of instructions in memory.
When you call the function, execution of the program jumps to those instructions, and when the
function returns, execution jumps back to the next line in the calling function. If you call the
function 10 times, your program jumps to the same set of instructions each time. This means
there is only one copy of the function, not 10.
If a function is declared with the keyword inline, the compiler does not create a real function: it
copies the code from the inline function directly into the calling function. No jump is made; it is
just as if you had written the statements of the function right into the calling function.
If the function is called 10 times, the inline code is copied into the calling functions each of those
10 times. The execution speed increases, but size of executable program increases too.
Syntax of declaration is:
inline <datatype> <class>_<function> (<parameters>) { <statements>; }
Inline functions are best reserved for small, frequently used functions.
For example, a function that return the maximum of two integers :
Delia Ungureanu - UNITBV
90
Functions
inline int Max (int a, int b)
{
return a > b ? a : b;
}
The effect of this is that when Max is called, the compiler, instead of generating code to call Max,
expands and substitutes the body of Max in place of the call. While essentially the same
computation is performed, no function call is involved and hence no stack frame is allocated.
Note: Inline is a hint to the compiler that you would like the function to be inlined.
The compiler is free to ignore the hint and make a real function call.
Use of inline for excessively long and complex functions is almost certainly ignored by the
compiler.
Recursion
A function can call itself. A function which calls itself is said to be recursive. Recursion can be
direct or indirect. It is direct when a function calls itself; it is indirect recursion when a function
calls another function that then calls the first function.
Recursion is a general programming technique applicable to problems which can be defined in
terms of themselves.
Take the factorial problem which is defined as:
Factorial of 0 is 1.
The second line clearly indicates that factorial is defined in terms of itself and hence can be
expressed as a recursive function:
long fact (unsigned int n)
{
if (n<=1)
return 1;
else
return n*Fact(n-1);
}
For n set to 3, Figure 13 provides a trace of the calls to Factorial. The stack frames for these
calls appear sequentially on the runtime stack, one after the other.
Delia Ungureanu - UNITBV
91
Functions
Figure 13 Factorial(3) execution trace.
A recursive function must have at least one termination condition which can be satisfied.
Otherwise, the function will call itself indefinitely until the runtime stack overflows. The Fact
function, for example, has the termination condition n <= 1 which, when satisfied, causes the
recursive calls to fold back. (Note that for a negative n this condition will never be satisfied and
Factorial will fail).
As a general rule, all recursive functions can be rewritten using iteration. An iterative version is
therefore preferred in this case:
long fact (unsigned int n)
{
long res = 1;
for(int i=1; i<=n;i++)
res = res*i;
return res;
}
Default Arguments
For every parameter you declare in a function prototype and definition, the calling function must
pass in a value. The value passed in must be of the declared type. Thus, if you have a function
declared as
int func1(int);
the function must in fact take an integer variable. If the function definition differs, or if you fail to
pass in an integer, you will get a compiler error.
The one exception to this rule is if the function prototype declares a default value for the
parameter. A default value is a value to use if none is supplied. The preceding declaration could
be rewritten as
int func1 (int a = 10);
Delia Ungureanu - UNITBV
92
Functions
This prototype says, "func() returns a long and takes an integer parameter. If an argument is not
supplied, use the default value of 10." Because parameter names are not required in function
prototypes, this declaration could have been written as
int func1 (int = 50);
The function definition is not changed by declaring a default parameter. The function definition
header for this function would be
int func (int a)
If the calling function did not include a parameter, the compiler would fill x with the default
value of 10. The name of the default parameter in the prototype need not be the same as the name
in the function header; the default value is assigned by position, not name.
Any or all of the function's parameters can be assigned default values. The one restriction is this:
If any of the parameters does not have a default value, no previous parameter may have a default
value.
If the function prototype looks like
int func (int par1, int par2, int par3);
you can assign a default value to par2 only if you have assigned a default value to par3. You can
assign a default value to par1 only if you've assigned default values to both par2 and par3. Listing
5.7 demonstrates the use of default values.
Listing 34 Function with default arguments.
1
2
3
4
5
6
7
8
9
10
11
12
13
93
Functions
Comments:
On line 4, the f() prototype specifies that the f() function takes three parameters.
The last two have default values. This function prints values passed to parameters.
On line 9 function is called with three arguments, so i is assigned with 3, j with 5 and
r with 1.5.
On line 10 f is called with two arguments: i is assigned with 3, j with 5 and r receives
the default value, 2.5.
On line 11 f is called with one argument, so i is assigned with 3, j and r receive the
default values, 25, respectively 2.5.
On line 12 f is called without argument. This produce an error with the message: too
few parameter in call to f(); i is not default parameter, so it must receive value.
A default argument need not necessarily be a constant. Arbitrary expressions can be used, so long
as the variables used in the expression are available to the scope of the function definition (e.g.,
global variables).
The accepted convention for default arguments is to specify them in function declarations, not
function definitions. Because function declarations appear in header files, this enables the user of
a function to have control over the default arguments. Thus different default arguments can be
specified for different situations. It is, however, illegal to specify two different default arguments
for the same function in a file.
Overloading Functions
C++ enables to create more than one function with the same name. This is called function
overloading. The functions must differ in their parameter list, with a different type of parameter,
a different number of parameters, or both. Here's an example:
int func (int, int);
int func (long, long);
int func (long);
is overloaded with three different parameter lists. The first and second versions differ in
the types of the parameters, and the third differs in the number of parameters.
func()
The return types can be the same or different on overloaded functions. You should note that two
functions with the same name and parameter list, but different return types, generate a compiler
error.
Function overloading is also called function polymorphism (poly means many, and morph means
form).
You can give two or more functions the same function name, and the right one will be called by
Delia Ungureanu - UNITBV
94
Functions
matching the parameters used.
Listing 35 illustrates the use of function overloading.
Listing 35 A demonstration of function polymorphism.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*overloading functions*/
#include <iostream.h>
#include<string.h>
int sum(int a,int b)
{ return a+b; }
double sum(double a, double b)
{ return a+b;}
char * sum(char *a, char *b)
{return strcat(a,b);}
void main()
{
cout << "42+17 = " << sum(42,17)<<endl;
cout << "42.0+17.0 = " << sum(42.0,17.0) <<endl;
cout << " C++ + is the best = " <<sum("C++ ", "is the best !")
<<endl;
}
C++ has a sequence for parameter matching to decide which overloaded function to call.
You can't declare two functions with the same parameter list, the call would be ambiguous.
C++ first checks to see whether there is a function that is an exact match. If there is not, it checks
for all possible functions to which it can convert the arguments.
C++ works through the sequence:
1. It looks for an exact match, including default parameters. If only one function is found,
use it. If more than one function is found, report ambiguous function error.
2. It looks for all matches through C++ automatic type conversion (called promotion). If
only one function is found, use it. If more than one function is found, report ambiguous
function error.
3. It looks for user-specified conversions. (In C++, you'll see that you can invent our own
types called classes.) Perform matching again.
95
Functions
The process becomes even more complicated with multiple parameters, but basically if it does
not find an exact match; it looks at all the nearest matches and chooses the function where the
most parameters match exactly.
96
In addition to the regular data types such as int, float, and double, you can now have data types
called as you want and.
You've already learned about a number of variable types, such as int, char, float and so on.
If you declare variables, the type of these variables tells you:
Typedef
The typedef keyword is used to create a new name for an existing data type. In effect, typedef
creates a synonym. For example, the statement
typedef int integer;
creates integer as a synonym for int. You then can use integer to define variables of type int, as in
this example:
integer count;
Note that typedef doesn't create a new data type; it only lets you use a different name for a
predefined data type.
Other examples:
typedef char string[20];
typedef unsigned int uint;
typedef int bool;
The effect of these definitions is that string becomes an alias for an array of 20 chars, uint
becomes an alias for unsigned int, and bool becomes an alias for int. Therefore:
97
name;
uint
x;
bool
OK;
Enumerations
enum keyword is used to define a set of constants of type int, called an enumeration data type.
To define an enumeration we use the syntax:
enum [<enum_name>] {<constant_name> [= <value>], ...} [var_list];
where:
<enum_name> is an identifier that names the set.
<constant_name> is the name of a constant that can optionally be assigned the value
where <prev> is the value of the previous integer constant in the list. For the first
integer constant in the list, the default value is 0.
<var_list> is an optional variable list that assigns variables to the enum type.
For example,
enum bool (false, true);
bool isOdd;
introduces an user-defined type (i.e. the type bool) for representing boolean values, two
enumerators which have integral values starting from 0 (i.e., false is 0 and true is 1) Unlike
symbolic constants, however, which are read-only variables, enumerators have no allocated
memory. isOdd is a variable of type bool.
enum days (sun=1, mon, tues, wed, thur, fri, sat) oneDay;
establishes an integer type named days, a variable oneDay of type days, and a set of enumerators
starting from 1(sun is 1, mon is 2, etc.).
You can omit <enum_type> if no further variables of this enum type are required:
enum {north = 10, south, east = 0, west} dir1, dir2;
introduces four constants (i.e., north is 10, south is 11, east is 0 and west is 1);
98
/*using enumerations*/
#include <iostream.h>
enum Month {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};
char* MonthStr (Month month)
{
switch (month) {
case Jan:
return "January";
case Feb:
return "February";
case Mar:
return "March";
case Apr:
return "April";
case May:
return "May";
case Jun:
return "June";
case Jul:
return "July";
case Aug:
return "August";
case Sep:
return "September";
case Oct:
return "October";
case Nov:
return "November";
case Dec:
return "December";
default:
return "";
}
}
int main (void)
{
cout << MonthStr(Aug) << '\n';
cout << MonthStr(5) << '\n';
return 0;
}
Comments:
On line 4 are defined the user-type named Month and constants: Jan with value 0, Feb
with value 1, etc.
On line 6 is defined the function MonthStr. This function has a parameter of type
Month.
switch statement (lines 8-21) use constants defined with enum Month.
You can call function MonthStr with an integer argument (see line 26, 27);
99
Structures
Structures are a very important part of C++. In C, structures provided a way to represent a
collection of data. In C++, structures are far more important because they are a fundamental
building block to representing an object. A structure represents the data needed to describe an
object. In the next chapter, you will explore the concepts behind object-oriented programming.
But first, you will explore the mechanics of structures.
Arrays (and also pointers to data lists) are a powerful method of grouping a lot of data together,
but all data elements must be of the same data type. If you need grouping elements of different
types, you must use structures.
A structure is a collection of one or more variables grouped under a single name for easy
manipulation. A structure can contain any of C++'s data types, including arrays and other
structures.
Note:
Though both <type-name> and <structure-variables> are optional, one of
the two must appear.
You define elements in the record by naming a <type>, followed by one or more <variablename> (separated by commas).
Each variable within a structure is called a member of the structure.
Note:
Members are often called fields in other programming languages and in
database systems.
Separate different variable types by a semicolon.
For example:
struct exStru { int m1;
char m2;
float m3; } var1,var2 ;
Delia Ungureanu - UNITBV
100
Once you create a struct, then you can make many instances of this new type of variable
youve defined. To declare additional variables of the same type, use the < type name> followed
by the variable names, for example:
exStru var3, var4;
Other example:
If you're writing a graphics program, your code needs to deal with the coordinates of points on
the screen. Screen coordinates are written as an x value, giving the horizontal position, and a y
value, giving the vertical position. You can define a structure named coord that contains both the
x and y values of a screen location as follows:
struct coord {
int x;
int y;
};
101
To display the screen locations stored in the structure point1, you could write:
cout<< x= << point1.x <<, y= <<point1.y;
Initializing Structures
Like other C++ variable types, structures can be initialized when they're declared. This procedure
is similar to that for initializing arrays. The structure declaration is followed by an equal sign and
a list of initialization values separated by commas and enclosed in braces. For example, look at
the following statement:
struct exStru { int m1;
char m2;
float m3; } myVar = { 99, y, 11.111} ;
The values are placed in the structure members in the order in which the members are listed in
the structure definition.
Pointers to Structures
A C++ program can declare and use pointers to structures, just as it can declare pointers to any
other data storage type. To create and use pointers to structures, first, define a structure:
struct exStru { int m1;
char m2;
float m3; };
Remember, the indirection operator (*) in the declaration says that pStru is a pointer to type
exStrut, not an instance of type exStru.
The pointer pStru can be used only if it was initialized. Because a pointer needs a memory
address to point to, you must declare an instance of type exStru before anything can point to it.
Here's the declaration:
Delia Ungureanu - UNITBV
102
Applying this to the current example, you know that pStru is a pointer to the structure myVar, so
*p_part refers to myVar. You then apply the structure member operator (.) to access individual
members of myVar. To assign the value 100 to myVar.m1, you could write
(*pStru).m1 = 100;
*pStru must be enclosed in parentheses because the (.) operator has a higher precedence than the
(*) operator.
The second method to access structure members using a pointer to the structure is to use the
indirect membership operator, which consists of the characters -> (a hyphen followed by the
greater-than symbol C++ treats them as a single operator, not two.) This symbol is placed
between the pointer name and the member name. For example, to access the number member of
myVar with the pStru pointer, you would write
pStru->m1;
Using a pointer to the structure with the indirect membership operator (->)
int x;
int y; };
103
coord topleft;
coord bottomright;
};
This statement defines a structure of type rectangle that contains two structures of type coord.
These two type coord structures are named topleft and bottomright.
The preceding statement defines only the type rectangle structure. To declare a structure, you
must then include a statement such as
rectangle box;
To access the actual data locations (the type int members), you must apply the member operator
(.) twice. Thus, the expression
box.topleft.x
refers to the x member of the topleft member of the type rectangle structure named box.
Figure 15.shows the relationship between the type rectangle structure, the two type coord
structures it contains, and the two type int variables each type coord structure contains.
Figure 15 Allocation of memory for structure rectangle.
Listing 1 takes input from the user for the coordinates of a rectangle and then calculates and
displays the rectangle's width, length and area.
104
int x;
int y;
};
struct rectangle{
coord topleft;
coord bottomright;
};
void main()
{
int length, width;
long area;
rectangle box;
/* Input the coordinates */
cout <<"\nEnter the top left x coordinate: ";
cin >> box.topleft.x;
cout <<"\nEnter the top left y coordinate: ";
cin >> box.topleft.y;
cout <<"\nEnter the bottom right x coordinate: ";
cin >> box.bottomright.x;
cout <<"\nEnter the bottom right y coordinate: ";
cin >> box.bottomright.y;
/* Calculate and display the length and width of the box */
width = box.bottomright.x - box.topleft.x;
length = box.bottomright.y - box.topleft.y;
cout << "\nThe width is: "<< width ;
cout << "\nThe length is: "<< length;
/* Calculate and display the area */
area = width * length;
cout << "\nThe area is: " << area;
}
Comments:
The coord structure is defined in lines 4 through 6 with its two members, x and y.
Lines 7 through 9 declare the rectangle structure. The two members of the rectangle
structure are topleft and bottomright, both structures of type coord.
Line 14 defines an instance, called box, of the rectangle structure.
Lines 17 through 24 fill in the values in the box structure. Each of box's members has
its own members. topleft and bottomright have two members each, x and y from the
coord structure. This gives a total of four members to be filled.
Delia Ungureanu - UNITBV
105
While memory allows, you can define structures that contain structures that contain structures, so
on. Rarely are more than three levels of nesting used in any C++ program.
char name[10];
int marks[4];
float average;
};
define a structure of type student that contains a 10-element character array member named
name, a four-element integer array member named marks and a float element named average.
You can then declare a structure named stud1 of type student as follows:
student stud1;
106
Character arrays are most frequently used to store strings. The name of an array, without
brackets, is a pointer to the array.
stud1.name
is a pointer to the first element of array name[] in the structure record. Therefore, you could print
the contents of name[] on-screen using the statement
puts(stud1.name);
or
cout << stud1.name;
To calculate and print on the screen the member average using values of elements of member
marks, you can write next statements:
stud1.average= 0;
for (int i=0; i<4; i++)
stud1.average += stud1.marks[i];
stud1.average /= 4;
cout <<\naverage = << stud1.average;
{
int *value;
int *rate;
}myVar;
These statements define and declare a structure whose two members are both pointers to type int.
As with all pointers, declaring them is not enough; you must, by assigning them the address of a
variable, initialize them to point to something. If cost and interest have been declared to be type
int variables, you could write
myVar.value = &cost;
myVar.rate = &interest;
Now that the pointers have been initialized, you can use the indirection operator (*). The
expression *myVar.value evaluates to the value of cost, and the expression *myVar.rate
evaluates to the value of interest.
Delia Ungureanu - UNITBV
107
Each pointer member of the structure points to the first byte of a string, stored elsewhere in
memory.
You can use pointer structure members anywhere a pointer can be used. For example, to print the
pointed-to strings, you would write
cout << myVar.msg1;
You can use an array of type char as a structure member and you can use a pointer to type char,
too. These are both methods for "storing" a string in a structure, as shown here in the structure
msg, which uses both methods:
struct msg {
char msg1[30];
char *msg2;
} myVar;
An array name without brackets is a pointer to the first array element. Therefore, you can use
these two structure members in similar fashion:
strcpy(myVar.msg1, "errors in program");
strcpy(myVar.msg2, "conditions are suspicious");
If you define a structure that contains an array of type char, every instance of that structure type
contains storage space for an array of the specified size. For example, the member msg1 is
limited to the specified size, 30 characters; you can't store a larger string in the structure.
If, on the other hand, you define a structure that contains pointers to type char, as member msg2,
these restrictions don't apply. Each instance of the structure contains storage space for only the
pointer. The actual strings are stored elsewhere in memory. There's no length restriction or
wasted space. The actual strings aren't stored as part of the structure. Each pointer in the structure
can point to a string of any length. That string becomes part of the structure, even though it isn't
stored in the structure.
108
This statement declares an array named points that contains 100 elements. Each element is a
structure of type coord and is identified by subscript like other array element types. Each of these
structures has two elements, each of which is of type int. This entire declaration is diagrammed in
Figure 17.
Figure 17 Organization of array points with elements of type coord.
When you have declared the array of structures, you can manipulate the data in many ways.
For example, to assign the data in one array element to another array element, you would write
points[0] = points[99];
This statement assigns to each member of the structure points[0] the values contained in the
corresponding members of points[99].
You can also move data between individual structure members. The statements
points[0].x = points[99].x;
Recall that the name of an array without brackets is a pointer to the first array element, so the
second line could also have been written as
pCoord = &points[0];
You now have an array of structures of type coord and a pointer to the first array element (that is,
the first structure in the array). For example, you could print the contents of the first element
using the statement
cout << pCoord->x, pCoord->y);
If you wanted to print all the array elements, you would probably use a for loop, printing one
Delia Ungureanu - UNITBV
109
If a pointer points to array element n, incrementing the pointer with the (++) operator causes it to
point to element n + 1. If the pointer pCoord was initialized to point to points[0]; each time
pCoord is incremented, it points to the next array element.
#include <iostream.h>
struct complex {double re,im;};
void reading_complex(complex *c)
{
cout<<"\nre=";
cin >> c->re;
cout<<"\nim=";
cin >> c->im;
}
110
Comments:
Line 3 defines the structure complex with two members of type double: re and im.
Line 5 through 11 define a function by passing the structure's address (parameter c is
a pointer to the structure complex). You must use the indirect membership operator (>) to access structure members in the function.
Line 13 through 19 define the function sum_complex that pass a parameter by value
and the other by reference. To refer the members of parameters you use the dot
operator (.). That function returns as value a structure complex.
Line 24 and 25 call the function reading_complex. It is necessary to use the address
of an object complex (i.e, &c1 and &c2).
Line 26 call the function sum_complex. It passes the argument c1 by value, and c2 by
reference. The value returned by function is assigned to variable of type complex, s.
Unions
Unions are similar to structures. A union is declared and used in the same ways that a structure is.
A union differs from a structure in that only one of its members can be used at a time. The reason
for this is simple. All the members of a union occupy the same area of memory. They are laid on
top of each other.
Data members of an union are mapped to the same address within its object. The size of an object
of a union is, therefore, the size of its largest data member.
The main use of unions is for situations where an object may assume values of different types,
but only one at a time.
Unions are defined and declared in the same fashion as structures. The only difference in the
Delia Ungureanu - UNITBV
111
Assuming that a float is 4 bytes, a int 2 bytes, and a char one byte, an object of type exUnion
would be exactly 4 bytes, i.e., the same as the size of a float (see Figure 18).
This union, exUnion, can be used to create instances of a union that can hold either a character
value c or an integer value i or a float value f. This is an OR condition. Unlike a structure that
would hold the third values, the union can hold only one value at a time. Figure 18 illustrates
how the shared union would appear in memory.
A union can be initialized on its declaration. Because only one member can be used at a time,
only one can be initialized. To avoid confusion, only the first member of the union can be
initialized. The following code shows an instance of the exUnion union being declared and
initialized:
exUnion var2 = {y};
Notice that the var2 union was initialized just as the first member of a structure would be
initialized.
Individual union members can be used in the same way that structure members can be used, by
using the member operator (.). However, there is an important difference in accessing union
members. Only one union member should be accessed at a time.
Because the members of the union share some memory, if you change the value of one member,
they will be changed the value of all members. Listing 39 illustrates how to use individual union
members and the effect of changing of values of members.
112
#include <iostream.h>
union exUnion {
char c;
int i;
float f;
};
void main(void)
{
exUnion myVar;
myVar.f=0;
cout <<" c= "<< myVar.c <<", i="<< myVar.i<<", f="<<myVar.f<<endl;
myVar.c= 97;
cout <<" c= "<< myVar.c <<", i="<< myVar.i<<", f="<<myVar.f<<endl;
}
Comments:
Initially you assign the value 0 to member f, so all members have zero values.
Modifying one member, they will be changed all members of unions.
Bit Fields
It is sometimes desirable to directly control an object at the bit level, so that as many individual
data items as possible can be packed into a bit stream without worrying about byte or word
boundaries.
A bit field is an element of a structure that is defined in terms of bits. Using a special type of
struct definition, you can declare a structure element that can range from 1 to 16 bits in length.
For example, this struct
struct bit_field
{
unsigned m1
: 1;
unsigned m2
: 4;
unsigned m3
: 1;
unsigned m4
: 10;
} myVariable;
113
A bit field is referred to in exactly the same way as any other data member.
myVariable.m1 =0;
myVariable.m2=7;
Because a bit field does not necessarily start on a byte boundary, it is illegal to take its address.
For the same reason, a bit field cannot be defined as static.
114
Object-Oriented Programming
Until now, the programming you have done has been "traditional" programming. C++ was treated
as a structural language and a procedural language.
In traditional, structured design, the data manipulated by a program and the functions that
manipulate the data are separate.
C++ language is a language that supports object-oriented programming and design. Objectoriented design involves classifying real-world objects and actions as classes that can be created,
manipulated, and destroyed.
The data that makes up an object, and the functions that are performed on that object, are
combined to form a class, or a description of that object. Classes can inherit functionality from
other objects, and you easily can add new classes that leverage existing classes.
So, the first things you will read about object-oriented programming are the three concepts:
Encapsulation
Inheritance
Polymorphism
Encapsulation
Encapsulation means hiding away the workings of your code.
Encapsulation means that you hide away the inner workings of your system and present a welldefined interface to the rest of the world that tells it only what it needs to know.
Writing good code means not only that it is fast and does what it is supposed to do, but also that it
can easily be maintained and amended. Hiding the workings of one piece of code away from
another helps the programmer change code, fix errors, or improve its workings without breaking
some piece elsewhere in the program.
This is transposed programming with C++ as creating a new data type that packages data and
functions.
Inheritance
Inheritance is the capability to borrow pieces of code to reuse.
Inheritance is the process of taking an object that does most of the job that you want it to do and
adding some extra bits to do a more useful or specialized job.
Delia Ungureanu - UNITBV
115
Object-Oriented Programming
You can take the existing class, clone it and make additions and modifications to the clone. This
is effectively what you get with inheritance, with the exception that if the original class (called
the base or super or parent class) is changed, the modified clone (called the derived or
inherited or sub or child class) also reflects those changes.
A class which adds new functionality to an existing class is said to derive from that original class.
The original class is said to be the new class's base class.
Polymorphism
Polymorphism is the capabilities to ask different objects to perform the same task and
have the object know how to achieve that task in its own way.
You have two ways to differentiate your new derived class from the original base class. The first
is quite straightforward: you simply add brand new functions to the derived class. These new
functions are not part of the base class interface. This means that the base class simply didnt do
as much as you wanted it to, so you added more functions. This simple and primitive use for
inheritance is, at times, the perfect solution to your problem. However, you should look closely
for the possibility that your base class might also need these additional functions. This process of
discovery and iteration of your design happens regularly in object-oriented programming.
Although inheritance may sometimes imply that you are going to add new functions to the
interface, thats not necessarily true. The second way to differentiate your new class is to change
the behavior of an existing base-class function. This is referred to as overriding that function.
To override a function, you simply create a new definition for the function in the derived class.
Youre saying Im using the same interface function here, but I want it to do something different
for my new type.
Object-oriented programming is a productivity tool. Each of these concepts is a step on the road
to reliable and productive programming. By using prebuilt libraries of code, you can save time
and still have the flexibility of altering the way that they work to suit your own needs.
Classes
A class allows data and functions to be bundled together and used as if they are a single element.
You make a new type by declaring a class. A class is just a collection of variables, often of
different types, combined with a set of related functions. Therefore, a data type consists of two
things:
A concrete representation of the objects of the type.
A set of operations for manipulating the objects.
Added to these is the restriction that, other than the designated operations, no other operation
should be able to manipulate the objects. For this reason, we often say that the operations
characterize the type, that is, they decide what can and what cannot happen to the objects. For the
same reason, proper data types as such are often called abstract data types abstract because
the internal representation of the objects is hidden from operations that do not belong to the type.
Delia Ungureanu - UNITBV
116
Object-Oriented Programming
Classes are similar to structures; in fact, classes really are just structures with a different name.
An instance of a class, sometimes called an object, is an occurrence of a class. An instance of one
of your classes can be used or manipulated inside your programs.
Classes and instances of classes are not the same things. Think of a class as the description of an
object; an instance of a class is a concrete occurrence of that class.
A class definition consists of two parts: header and body. The class header specifies the class
name and its base classes. (The latter relates to derived classes and is discussed in next lecture.)
The class body defines the class members. Two types of members are supported:
Data members have the syntax of variable definitions and specify the representation of
class objects. Member variables are part of your class.
Member functions have the syntax of function prototypes and specify the class operations,
also called methods. They are the class interface. They determine what the objects of your
class can do.
C++ uses three explicit keywords to set the boundaries in a class: public, private, protected,
also called access specifiers. These specifiers determine who can use the definitions that follow.
They determine the access permission categories:
Protected members are only accessible by the class members and the members of a derived
class.
Note: The default access permission for a class member is private.
The data type defined by a class is used in exactly the same way as a built-in type.
Declaring a Class
To declare a new type class, you use the syntax:
class <classname> [<:baselist>] { <member list> };
where:
class is a keyword.
<classname> can be any name unique within its scope.
<baselist> lists the base class(es) that this class derives from. <baselist> is optional
<member list> declares the class's data members and member functions.
117
Object-Oriented Programming
Listing 40 shows the definition of a simple class for representing points in two dimensions.
class point {
set xVal, yVal;
public:
void set( int, int);
int getx(void);
int gety(void);
void move(int dx, int dy);
void print(void);
};
Comments:
First line contains the class header and names the class as point. An open brace
marks the beginning of the class body.
Lines 2-9 contains the body of class point.
Line 2 defines two data members, xVal and yVal, both of type int. The default
access permission for a class member is private. So both xVal and yVal are private.
On line 3 is used keyword public to change access permission for followin g
members. From this point onward the class members are public.
On lines 4-8 are defined member functions. These functions have different type of
parameters and return different types.
The brace on line 9 marks the end of the class body.
You can declare the members in any order. You can use as much access permission specifiers in
any order.
118
Object-Oriented Programming
Listing 41 Example for Definition of Member Functions.
1
2
3
4
5
6
Comments:
The function name should be preceded by the class name and a double-colon. This
identifies set() as being a member of point. The function interface must match its
earlier interface definition within the class (i.e., take two integer parameters and have
the return type void). These functions being members of point is free to refer to xVal
and yVal. The non-member functions do not have permission to refer to xVal, yVal
because they are private.
Inline Implementation
Just as global functions may be defined to be inline, so can the member functions of a class. In
the class point, for example, both member functions are very short (only two statements).
Defining these to be inline improves the efficiency considerably. The keyword inline appears
before the return value.
inline int point::getx(void)
{
return xVal;
}
You can also put the definition of a function into the declaration of the class, which automatically
makes that function inline. For example,
class point {
// inline function
// inline function
};
119
Object-Oriented Programming
Comments:
In this example, two functions are completely defined inside the class. The both are
by default inline.
Defining an Object
Once a class is defined, its name denotes a new data type, allowing us to define variables of that
type. For example:
point Point;
defines two objects (p1 and p2) both of the same class (point). Furthermore, operations of a class
are applied to objects of that class, but never the class itself. A class is therefore a concept that
has no concrete existence other than that reflected by its objects.
You should clearly distinguish between object and class. A class denotes a type, of which there is
only one. An object is an element of a particular type (class), of which there may be many.
calls function set() for object p1. The arguments passed to functions are (5,10). So the values of
xVal and yVal are changed to 5, respectively 10. p1 is an implicit argument to set().
By making xVal and yVal private members of the class, we have ensured that a user of the class
cannot manipulate them directly:
p1.xVal = 5;
// illegal
Pointers to Classes
A C++ program can declare and use pointers to classes, just as it can declare pointers to
structures or any other data storage type.
Using class pointers is alike using structure pointers (see preview lecture).
To create and use pointers to classes, you use the indirection operator (*) in the declaration:
point * pp;
The pointer p_p can be used only if it was initialized. For example:
Delia Ungureanu - UNITBV
120
Object-Oriented Programming
point myVar;
pp =&myVar
or:
pp->set(2,5);
To write
(*pp).xVal=99;
or
pp->xVal = 99;
/* A Simple Class */
# include <iostream.h>
class point{
int xVal, yVal;
public:
void set( int x, int y) //inline
{
xVal=x;
yVal=y;
}
int getx(void)
// inline
{
return xVal;}
int gety(void);
void move(int dx, int dy);
void print(void);
};
inline int point::gety(void)
//inline
{
return yVal;}
void point::move(int dx, int dy)
{
xVal += dx;
yVal += dy;
}
void point::print(void)
{
cout<< "\nx="<< xVal;
cout<< "\ny="<< yVal;
}
121
Object-Oriented Programming
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void main(void)
{
point p1, p2, *pp;
p1.print();
p1.set(5,10);
p1.print();
p2 = p1;
p2.print();
p1.move(2,3);
p1.print();
pp = &p1;
cout <<"\np1 has x=" << pp->getx();
cout <<"\np1 has y=" << pp->gety();
}
Comments:
On line 3-15 is declared the class point with tow data members (xVal, yVal) of type
int with access private and five methods. Three of them are inline functions, set() and
getx() are completely defined inside the declaration of the class. The function gety()
is declared explicitly inside.
When you implement the functions gety(), move(), print() you must specify the name
of class which belong to and use the double-colon operator(::) (scop operator) in the
header of functions.
On line 29 are define two objects of type point and a pointer to point.
Line 30 calls the function print() for the p1 object. It will display the values of the
members, xVal and yVal, for p1. The values are residuals because they arent
initialized.
On line 31 function set assigns to xVal, yVal values;
On line 33 is used operator = to assign same values to the object p2. Assignation is
made member by member.
On line 37 the pointer pp is initialised with the address of p1. Further on p1 and *pp
will refer the same object.
this Pointer
Every class member function has a hidden parameter: the this pointer. this points to the
individual object. Therefore, in each call to set(), getx(), gety(), move(), the this pointer for the
object is included as a hidden parameter.
An example of keyword this use:
122
Object-Oriented Programming
class X {
int a;
public:
X (int b) {this -> a = b;}
In nonstatic member functions, the keyword "this" is a pointer to the object for which the
function is called. All calls to nonstatic member functions pass "this" as a hidden argument.
The keyword "this" is a local variable available in the body of any nonstatic member function.
Use it implicitly within the function for member references. It does not need to be declared and it
is rarely referred to explicitly in a function definition.
For example, in the call x.func(y), where y is a member of X, the keyword "this" is set to &x and
y is set to this->y, which is equivalent to x.y.
For example, you can add a member function in class point (see Listing 43) that displays the
memory address of the object.
class point {
//
void address()
{ cout <<"\nThe address of the object is: " << this;}
//
};
at the execution of program they will appear two identical messages like these:
The address of the object is: 0x25472248
The address of the object is: 0x25472248
Constructors
It is possible to define and at the same time initialize objects of a class. This is supported by
special member functions called constructors.
Note:
Note:
For example,
123
Object-Oriented Programming
class point {
int xVal, yVal;
public:
point (int x,int y) {xVal = x; yVal = y;} // constructor
void move (int,int);
};
is an alternative definition of the point class. The method set() is replaced by a constructor.
You can define objects of type point and initialize them at once. This is in fact compulsory for
classes that contain constructors that require arguments:
point p1 = point(5,10);
// illegal,
illegal they are missing the arguments
A class can have more than one constructor. They may be overloaded. To avoid ambiguity,
however, each of these must have a unique signature. For example,
class point {
int xVal, yVal;
public:
point (void)
// default constructor
{ xVal = yVal = 0; }
point (int x, int y)
{ xVal = x; yVal = y; }
point (float, float);
// polar coordinates
// ...
};
point::point (float len, float angle)
{
xVal = (int) (len * cos(angle));
yVal = (int) (len * sin(angle));
}
offers three different constructors. An object of type point can be defined using any of these:
point p0;
// origin
point p1(10,20);
// cartesian coordinates
124
Object-Oriented Programming
nothing.
As with global functions, a member function of a class may have default arguments. The same
rules apply: all default arguments should be trailing arguments, and the argument should be an
expression consisting of objects defined within the scope in which the class appears.
A constructor for the point class may use default arguments. For example,
class point {
int xVal, yVal;
public:
point (int x = 0, int y = 0);
//...
};
where the constructor with default arguments replace the two constructors point(void) and
point(int, int). The following definitions are all valid:
point
p1;
point
p2(10);
Ppint
p3(10, 20);
Careless use of default arguments can lead to undesirable ambiguity. For example, given the class
class point {
int xVal, yVal;
public:
point (int x = 0, int y = 0);
point (float x = 0, float y = 0);
// polar coordinates
//...
};
the following definition will be rejected as ambiguous, because it matches both constructors:
Point p;
// ambiguous!
ambiguous
It is important to note that an objects constructor is applied when the object is created. This in
turn depends on the objects scope. For example, a global object is created as soon as program
execution commences; an automatic object is created when its scope is entered; and a dynamic
object is created when the new operator is applied to it.
A special case of constructor is called the copy constructor. This special constructor always
looks like this:
ClassName(const ClassName& name)
This is a constructor that takes a reference to the class object itself as a parameter. This
constructor allows an object to be copied to another object of the same type at the time of
construction.
125
Object-Oriented Programming
For example,
class point {
int xVal, yVal;
public:
// ...
point (point & p)
{ xVal = p.xVal; yVal = p.yVal; }
// ...
};
Destructors
Just as a constructor is used to initialize an object when it is created, a destructor is used to clean
up the object just before it is destroyed.
Note:
A destructor always has the same name as the class itself, but is preceded
with a ~ (tilde) symbol.
Note:
Note:
A destructor never takes any arguments and has no explicit return type.
Destructors are generally useful for classes which have pointer data members which point to
memory blocks allocated by the class itself. In such cases it is important to release memberallocated memory before the object is destroyed. A destructor can do just that.
For example,
class point {
int xVal, yVal;
public:
point (int x = 0, int y = 0);
~point()
{ cout << Object is destroyed; }
//...
};
126
Object-Oriented Programming
In general, an objects destructor is applied just before the object is destroyed. This in turn
depends on the objects scope. For example, a global object is destroyed when program execution
is completed; an automatic object is destroyed when its scope is left; and a dynamic object is
destroyed when the delete operator is applied to it.
Static Members
A data member of a class can be defined to be static. This ensures that there will be exactly one
copy of the member, shared by all objects of the class. For example, consider a Window class
which represents windows on a bitmap display:
class point {
static int counter;
//...
};
Here, no matter how many objects of type point are defined, there will be only one instance of
counter. Like other static variables, a static data member is by default initialized to 0. It can be
initialized to an arbitrary value in the same scope where the member function definitions appear:
int point::counter = 0;
The alternative is to make such variables global, but this is exactly what static members are
intended to avoid; by including the variable in a class, we can ensure that it will be inaccessible to
anything outside the class.
Member functions can also be defined to be static. Semantically, a static member function is like
a global function which is a friend of the class, but inaccessible outside the class. It does not
receive an implicit argument and hence cannot refer to this. Static member functions are useful
for defining call-back routines whose parameter lists are predetermined and outside the control of
the programmer.
For example,
class point {
//...
static void displayCounter (void);
};
Because static members are shared and do not rely on the this pointer, they are best referred to
using the class::member syntax. For example, displayCounter() would be referred to as
point::displayCounter();
or
point p1::displayCounter();
127
Object-Oriented Programming
Friends
Occasionally we may need to grant a function access to the nonpublic members of a class. Such
an access is obtained by declaring the function a friend of the class.
The syntax is:
friend <identifier>;
Use friend to declare a function or class with full access rights to the private and protected
members of an outside class, without being a member of that class.
In all other respects, the friend is a normal function in terms of scope, declarations, and
definitions.
If you want to explicitly grant access to a function that isnt a member of the current classyou
must declare that function a friend inside the structure declaration. Its important that the friend
declaration occurs inside the class declaration.
You can declare a global function as a friend, and you can also declare a member function of
another class, or even an entire class, as a friend. Heres an example :
class point {
//...
friend double distance( point, point);
};
double distance(point p1, point p2)
{
The function distance calculate and return the distance between two points. It is declared friend to
class point inside the class, but it is not a member of point. It has access to private members of
point. It is a global function and can be called like:
cout << \nDistance between p1 and p2 is: << distance(p1, p2);
The statement:
p1.distance(p1,p2);
// abbreviated form
};
Although a friend declaration appears inside a class, that does not make the function a member of
that class. In general, the position of a friend declaration in a class is irrelevant: whether it
appears in the private, protected, or the public section, it has the same meaning.
Delia Ungureanu - UNITBV
128
Object-Oriented Programming
Overloading
The term overloading means providing multiple definitions of. Overloading of functions
involves defining distinct functions which share the same name, each of which has a unique
signature. Function overloading is appropriate for:
Defining functions which essentially do the same thing, but operate on different data types.
Providing alternate interfaces to the same function, you need different algorithms.
C++ allows functions to be overloaded, that is, the same function to have more than one
definition. When an overloaded function is called, the compiler compares the number and type of
arguments and chooses the one that matches the call. For example:
Comments:
On lines 4-5 is defined function g() with one parameter of type int and on lines 7-8 is
defined a function with the same name, g(), but with one parameter of type double.
On line 16 g() is called with an int argument, so it is called the first definition.
On lines 17 the type of argument is different of parameter type. It is made a
conversion of type float to double and it is called the second definition of function.
On line 18 is made a conversion char to int and is called the first definition.
If line 19 is active, it will produce an error message determinate by the ambiguity of
Delia Ungureanu - UNITBV
129
Object-Oriented Programming
conversion (long to int, or long to double);
To avoid ambiguity, each definition of an overloaded function must have a unique signature.
Member functions of a class may also be overloaded. A situation already mooted is overloading
of constructors.
Function overloading is useful for obtaining flavors that are not possible using default arguments
alone. Overloaded functions may also have default arguments.
Operator Overloading
In C++, its possible to define additional meanings for its predefined operators by overloading
them. This definition is just like an ordinary function definition except the name of the function
begins with the keyword operator and ends with the operator itself. Thats the only difference,
and it becomes a function like any other function, which the compiler calls when it sees the
appropriate pattern.
You can overload an operator using member functions or using global functions those are friends
of the class.
Defining an overloaded operator is like defining a function. The syntax used is:
<type> operator <operator symbol>( <parameters> )
{
<statements>;
}
where :
<type> is the data type returned by function;
operator is the keyword;
The number of arguments in the function argument list depends on two factors:
Whether its a unary (one argument) or binary (two argument) operator.
Whether the operator is defined as a global function (one argument for unary, two for
binary) or a member function (zero arguments for unary, one for binary the object
becomes the left-hand argument).
Although you can overload almost all the operators available in C++, the use is fairly restrictive.
The next five operators cannot be overloaded:
.
Delia Ungureanu - UNITBV
.*
::
?:
sizeof
// not overloadable
130
Object-Oriented Programming
The overloadable operators are:
unary:
binary:
&
++
--
()
->
->*
new
delete
&
<<
>>
+=
-=
/=
%=
&=
|=
^=
<<=
>>=
==
!=
<
>
<=
>=
&&
||
[]
()
A strictly unary operator (e.g., ~) cannot be overloaded as binary, nor can a strictly binary
operator (e.g., =) be overloaded as unary.
C++ does not support the definition of new operator tokens, because this can lead to ambiguity.
Furthermore, the precedence rules for the predefined operators is fixed and cannot be altered. For
example, no matter how you overload *, it will always have a higher precedence than +.
Operators ++ and -- can be overloaded as prefix as well as postfix. Equivalence rules do not hold
for overloaded operators. For example, overloading + does not affect +=, unless the latter is also
explicitly overloaded. Operators ->, =, [], and () can only be overloaded as member functions,
and not globally.
Note:
You cannot combine operators that currently have no meaning in C++
(such as ** to represent exponentiation).
Note:
Note:
Note:
Note:
+= = /= *= ^= &= |=
%=
To avoid the copying of large objects when passing them to an overloaded operator, references
Delia Ungureanu - UNITBV
131
Object-Oriented Programming
should be used. Pointers are not suitable for this purpose because an overloaded operator cannot
operate exclusively on pointers. If you only need to read from the argument and not change it,
default to passing it as a const reference.
The type of return value you should select depends on the expected meaning of the operator.
All the assignment operators modify the lvalue. To allow the result of the assignment to be used
in chained expressions, like A=B=C, its expected that you will return a reference to that same
lvalue that was just modified.
For the logical operators, everyone expects to get at worst an int back, and at best a bool if it is
defined.
/* Overloading operators */
# include <iostream.h>
enum bool {false, true};
class point {
int xVal, yVal;
public:
point( int x=0, int y=0)
{
xVal=x; yVal=y; }
void print(void);
point& operator++()
{
xVal++; yVal++;
return *this;
}
friend point& operator--(point&);
point& operator=(point&p)
{
xVal=p.xVal; yVal=p.yVal;
return *this;
}
bool operator==(point);
friend point operator+ (point,point);
};
void point::print(void)
{
cout<< "\nx="<< xVal;
cout<< "\ny="<< yVal;
}
bool point::operator==(point p)
{
if (p.xVal==xVal && p.yVal==yVal) return true;
return false;
}
point& operator--(point&p)
{
p.xVal--; p.yVal-- ;
return p; }
point operator+(point p1, point p2)
{
return point(p1.xVal+p2.xVal , p1.yVal+p2.yVal); }
132
Object-Oriented Programming
33
34
35
36
37
38
39
40
41
42
43
44
void main(void)
{
point p1(2,5), p2(7,11), p3;
p1++;
p1.print();
p3 = p1;
if (p1==p2)
cout << "\npoints are identical";
else
cout << "\npoints are different";
p3=p1+p2;
p3.print();
}
Comments:
On lines 10-12 is defined the unary operator ++ as member function.
The operator -- is defined with a non-member function. On line 13 it is declared as
friend of class point and on lines 28-30 is defined. Function must have one parameter.
Operator = is defined on lines 14-16. It is a binary operator and it is defined as
member. It cant be defined as non-function.
Binary operator = = is defined as member, so it has one parameter, while operator+ is
defined as friend function, so it has two parameters.
Type Conversion
In C and C++, if the compiler sees an expression or function call using a type that isnt quite the
one it needs, it can often perform an automatic type conversion from the type it has to the type it
wants. In C++, you can achieve this same effect for user-defined types by defining automatic
type-conversion functions. These functions come in two flavors: a particular type of constructor
and an overloaded operator.
In general, given a user-defined type X and another (built-in or user-defined) type Y:
A constructor defined for X which takes a single argument of type Y will implicitly convert
needed.
class X {
//...
X (Y&);
// convert Y to X
operator Y ();
// convert X to Y
};
133
Object-Oriented Programming
Constructor conversion
If you define a constructor that takes as its single argument an object (or reference) of another
type, that constructor allows the compiler to perform an automatic type conversion. For example,
class One
{
public:
One() {}
};
class Two
{
public:
Two(const One&) {}
};
void f(Two) {}
int main()
{
One one;
f(one);
}
When the compiler sees f( ) called with a One object, it looks at the declaration for f( ) and
notices it wants a Two. Then it looks to see if theres any way to get a Two from a One, and it finds
the constructor Two::Two(One), which it quietly calls. The resulting Two object is handed to f( ).
In this case, automatic type conversion has saved you from the trouble of defining two
overloaded versions of f( ). However, the cost is the hidden constructor call to Two, which may
matter if youre concerned about the efficiency of calls to f( ).
In Listing 45 is defined the constructor
point( int x=0, int y=0)
{
xVal=x; yVal=y; }
is equivalent to
point p=point(7);
The effect is an implicit type conversion from int to point. xVal takes the value 7 and yVal
takes 0.
Operator conversion
The second way to effect automatic type conversion is through operator overloading. You can
create a member function that takes the current type and converts it to the desired type using the
operator keyword followed by the type you want to convert to.
If we want to do the conversion from the class type to another type, constructors cannot be used
Delia Ungureanu - UNITBV
134
Object-Oriented Programming
because they always return an object of the class to which they belong. Instead, one can define a
member function which explicitly converts the object to the desired type.
For example, given a rectangle class, we can define a type conversion function which converts
a rectangle to a point, by overloading the type operator point in rectangle:
class rectangle {
point
topLeft;
point
botRight;
public:
rectangle (int left, int top, int right, int bottom);
rectangle (point &p, point &q);
//...
operator point ()
};
This operator is defined to convert a rectangle to a point, whose coordinates represent the width
and height of the rectangle. Therefore, in the code fragment
point
p(5,5);
rectangle
r(10,10,20,30);
r + p;
rectangle r is first implicitly converted to a point object by the type conversion operator, and
then added to p.
The type conversion point can also be applied explicitly using the normal type cast notation. For
example:
point(r);
(point)r;
135
Inherita nce
In practice, most classes are not entirely unique, but rather variations of existing ones. Consider,
for example, the class named point for representing points in two dimensions, and another class
named circle for representing circles with the both coordinates of center and the radius. These
two classes would have much in common. For example, they would have similar member
functions such as move(),getx(), gety(), as well as similar data members such as xVal, yVal.
Some of the member functions in both classes would therefore be identical, while a few would be
different. For example, function print() would be different for the two classes, while a function
aria(), that calculates aria of circle can be defined only in class circle.
Given the shared properties of these two classes, it would be tedious to have to define them
independently. Clearly this would lead to considerable duplication of code. The code would not
only take longer to write it would also be harder to maintain: a change to any of the shared
properties would have to be consistently applied to both classes.
Object-oriented programming provides a facility called inheritance to address this problem.
Under inheritance, a class can inherit the properties of an existing class. Inheritance makes it
possible to define a variation of a class without redefining the new class from scratch. Shared
properties are defined only once, and reused as often as desired.
In C++, inheritance is supported by derived classes. A derived class is like an ordinary class,
except that its definition is based on one or more existing classes, called base classes. A derived
class can share selected properties (function as well as data members) of its base classes, but
makes no changes to the definition of any of its base classes. A derived class can itself be the
base class of another derived class. The inheritance relationship between the classes of a program
is called a class hierarchy.
A derived class is also called a subclass, because it becomes a subordinate of the base class in the
hierarchy. Similarly, a base class may be called a superclass, because from it many other classes
may be derived.
When you declare a derived class, you give the name of the class, as usual, but before the
opening brace of the class body, you put a colon and the name of the base class (or classes, for
multiple inheritance). When you do this, you automatically get all the data members and member
functions in the base class.
To declare a new type class, you use the syntax (see lecture 6):
class <classname> [<:baselist>] { <memberlist> };
int radius;
public:
// . . .
};
Although the private members of a base class are inherited by a derived class, they are not
accessible to it. For example, circle inherits all the private (and public) members of point, but
Delia Ungureanu - UNITBV
136
Object-Oriented Programming
is not allowed to directly refer to the private members of point. The idea is that private members
should be completely hidden so that they cannot be tampered with by the class clients.
The restriction can be relaxed by defining the base class private members as protected instead. As
far as the clients of a class are concerned, a protected member is the same as a private member: it
cannot be accessed by the class clients. However, a protected base class member can be accessed
by any class derived from it.
For example, the private members of point can be made protected by substituting the keyword
protected for private:
class point {
protected:
int xVal, yVal;
public:
//...
};
The access keywords private, public, and protected can occur as many times as desired in a
class definition. Each access keyword specifies the access characteristics of the members
following it until the next access keyword:
class ex {
public:
// public members...
private:
// private members...
protected:
// protected members...
public:
// more public members...
protected:
// more protected members...
};
A base class may be specified to be private, public, or protected. Unless so specified, the base
class is assumed to be private:
class A {
private:
int x;
void Fx (void);
public:
int y;
void Fy (void);
protected:
int z;
void Fz (void);
};
137
Object-Oriented Programming
class B : A {};
So, x and Fx becomes private members of D, y and Fy become public members of D, and z
and Fz become protected members of D.
The private members of a protected base class become
Private Derived
Public Derived
Protected Derived
Private Member
private
private
private
Public Member
private
public
protected
Protected Member
private
protected
protected
Thus, private inheritance is useful only if you want to hide part of the functionality of the base
class.
protected:
int xVal, yVal;
public:
point( int x=0, int y=0)
{ xVal=x; yVal=y; }
void move(int x, int y)
{ xVal+=x; yVal+=y;}
void print(void);
};
138
Object-Oriented Programming
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Comments:
On lines 4-12 is defined class point. The access to xVal, yVal is protected to permit
the access of all derived class.
On line 14 is write the header of class circle. The access to base class is public.
The class circle inherits xVal, yVal as private access member and point(), move(),
print() as public access members.
circle has new members: data member radius and function members area(), circle().
The function print() is overloaded. If it is called by an point object, it is used the
definition of class point and if it is called by an circle object, it is used the definition
of class circle. So on line 43 is called the function declared on line 22.
On line 17 are specified the parameters passed to point constructor. It uses a member
Delia Ungureanu - UNITBV
139
Object-Oriented Programming
initialization list in the definition of the constructor. Otherwise the constructor of
point is called with default values, 0 and 0. The effect is that here members are
initialized before the body of the constructor is executed.
Note:
A member initialization list may be used for initializing any data member
of a class. It is always placed between the constructor header and body. A colon is
used to separate it from the header. It should consist of a comma-separated list of data
members whose initial value appears within a pair of brackets.
Type Conversion
For any derived class there is an implicit type conversion from the derived class to any of its
public base classes. This can be used for converting a derived class object to a base class object,
be it a proper object, a reference, or a pointer:
Such conversions are safe because the derived class object always contains all of its base class
objects.
Listing 47
1 void
2 {
3
4
5
6
7
8
9
10 }
main(void)
// . . .
circle c1(5,10,20);
point p1, *pp;
p1 = c1;
p1.printf();
*pp= &c1;
pp->printf();
point &rp = c1;
rp.printf();
// . . .
In Listing 47 the first assignment, for example, causes the circle component of c1 to be
assigned to p1. They are assigned only the member of base class. The second and third
assignations are legal, but using pp or rp you can access only the base class member. Function
print() called is that witch is defined in class point, so will display only xVal and yVal.
By contrast, there is no implicit conversion from a base class to a derived class. The reason that
such conversion is potentially dangerous due to the fact that the derived class object may have
data members not present in the base class object. The extra data members will therefore end up
with unpredictable values. All such conversions must be defined by you (see paragraph Type
conversion in Lecture 6).
Note:
A base class object cannot be assigned to a derived class object unless
there is a type conversion constructor in the derived class defined for this purpose.
Delia Ungureanu - UNITBV
140