Vous êtes sur la page 1sur 31

Part I Easy Does It

Chapter 1: Basics For Beginners

Chapter 2: Simple Pointers And Functions

Chapter 3: Classes, The Basis Of Object-Oriented-Programming

1
Chapter 1: Basics For Beginners

1. #include < >


1.1 Software requirements 2. int main()
(alternatively just write main(), its the same!)
You will need the following to write C++ programs: 3. open bracers, {
1. a text editor which is something like a word processor 4. return 0;
(like your notepad), (Non-zero integers, eg -1 or 1, can be returned if you
2. a compiler which is a piece of software that checks the intend to exit the program while indicating that an error
source code for errors and converts it into a piece of code has been encountered.)
known as object code, 5. close bracers, }
3. a linker another piece of software that takes the object
code and uses it to join together ready-made low-level
routines to produce the executable code that will run on
1.2.2 Displaying a simple message
the computer.
It is a great morale booster if you could display something, in fact
anything, on the computer screen. So for the sake of your morale,
You do not have to worry about installing each piece of software
separately if you install Microsoft Visual C++. All items are lets write a program that displays Hello world!.
bundled together. This is called an integrated development
environment (IDE). Let me introduce to you:
std::cout sends data to the screen
<< known as the insertion operator and inserts anything
that follows it into the output stream.
1.2 C++ Basics std::endl end of line. Alternatively, may be replaced with
\n
1.2.1 General layout of program My first C++ // comments or remarks for the programmer. The C++
program! compiler ignores the comments.

#include <iostream> Listing 1.1

int main() #include <iostream>


{
// Your fantastic program here int main()
return 0; {
} std::cout << Hello world! << std::endl;
return 0;
If youve noticed, a typical C++ program would have }

2
1.2.4 Simple arithmetic
To make this program work,
1. Type the codes shown above. int A;
2. Save the code to disk. You should always do this. The file int B, C;
extension should be .cpp (c-plus-plus).
3. Compile the program. A = 5;
4. Build the program. B = 2;
// Add
5. Run the program.
C = A + B;
// Subtract
The console will display Hello world! C = A B;
// Multiply
C = A * B;
1.2.3 Data types and variables // Divide (A bit tricky)
C = A / B;
Let me introduce to you 5 basic data types: // Remainder
C = A % B; // 5 / 2 = 2 remainder 1
int integers, i.e., ,-3,-2, -1, 0, 1, 2, 3, // Thus, C = remainder = 1
float they are real numbers which may contain decimal points.
double like float, they are real numbers but with more The divide statement will not produce 1.5 as you would expect.
precision. Instead, it gives 1. Do you know why?
char character, they include keyboard entries.
bool boolean, takes in either true (1) or false (0) values. Since C is declared as an integer, it will only store the quotient (the
whole bit) of the result of the division and ignores the mantissa
You need to declare all variables that you intend to use with a data (the decimal bit).
type. The variable must have a unique name. Remember that C++
is case sensitive, so the variable number is not the same as First solution to get C = A / B = 1.5 is to declare all A, B,
NuMBeR. You can initialize the variable (i.e., to assign an initial and C as floats. Even if C is a float but A and B are integers, you
value to the variable) either during declaration: will still be getting C = 1!
int A = 10;
or after declaration, i.e.,
int A;
Second solution (if A and B must be integers, and C is a float) is
A = 10; to perform a cast.

For float values, remember to add the letter f or F (for float) C = (float) A / B;
behind the value. To declare the variable pi as a float and
initialize it with a value of 3.14: Casting is the process of forcing the variable from one data type to
change to one of another data type.
float pi = 3.14f;

3
1.2.5 Useful shorthands for simple arithmetic When the array size is left unspecified, the C++ compiler figures
out the dimension of m from the number of data items supplied.
A++; is shorthand for (s.h.) A = A + 1; You can pre-allocate more memory than you would initially
++A; s.h. A = A + 1; require:
A--; s.h. A = A 1;
--A; s.h. A = A 1; int arr[10] = {1,2,3,4};
A += B; s.h. A = A + B;
A -= B; s.h. A = A B; The size of arr is 10. It is initialized with only 4 elements for
A *= B; s.h. A = A * B; now. The remaining memory slots can be assigned some values
A /= B; s.h. A = A / B; later.
A += B++; s.h. A = A + B;
B = B + 1;
1.2.7 String of Characters
A += ++B; s.h. B = B + 1;
A = A + B; Use char to initialize a single character:

1.2.6 Arrays char a_letter = A;

and a string of characters well, there are two ways of doing so:
An array is composed of consecutive memory cells that are
allocated to store data of a certain type. The array
1.2.7.1 Using a char array
int m[5];
char girl[10] = Kelly;
declares m as an array and defines for it 5 cells (memory blocks)
Note single quotes for single characters and double quotes
where each can be accessed as m[0], m[1], , m[4]. Each
for string-of-characters (strings).
cell is large enough to hold a quantity of type int, which is
exactly 4-bytes. Initialization can be done on a per-cell basis : An array of characters is declared and its elements are initialized as
m[0] = 2;
girl[0] = K
m[1] = 3;
girl[1] = e
m[2] = 5;
girl[2] = l
m[3] = 7;
girl[3] = l
m[4] = 11;
girl[4] = y
girl[5] = \0.
or all at once at declaration:
The last character is known as the null character. Its usage is to
int m[] = {2,3,5,7,11};
inform the C++ compiler that the string will be terminated at this

4
point. Thus, do not forget to allocate space for this character. This If you include string, you can also make use of several
is equivalent to: helpful string related functions such as

char girl[10] = strcpy(girl, Jac);


{K, e, l, l, y, \0}; Reads as string copy.
Copies the string Jac into the variable, name.
This is also an example of an over-sized array where the girl Command will overwrite previous values, if any.
array is set to be of size 10 but only 6 characters are actually strcat(girl, , Jill);
required. This is perfectly alright because the null character is Reads as string concatenate (add). Appends , Jill to
present to inform the C++ compiler that the string ends there. If for the original value Jac. Thus girl becomes Jac,
any reason you would like to add characters behind Kelly, you Jill.
need to shift the null character to the end of the string. For strcmp(girl, May) // No semicolon
example, if you want to change girl to Kelly Tan. Reads as string compare.
Returns 1 if the name matches May and 0 if not.
girl[5] = ; // Replace \0 with space if (strcmp(girl, May) == 1)
girl[6] = T; cout << Hi May!\n;
girl[7] = a;
girl[8] = n; strlen(girl) // No semicolon
girl[9] = \0;. Returns the length of the string
int len = strlen(girl);
In addition, you also do not have to pre-determine the size of the
string array. You can leave out its size and the C++ compiler will There are other useful character manipulation functions for single
automatically figure that out! characters in another header file called ctype.h.
char girl[] = Kelly; // Array of size 6 #include <ctype.h>

If instead of initializing the string with a value, you can first If you do include this header file, you can use the following
declare the variable, commands:
char girl[10]; toupper(a_letter)
Returns an upper-cased a_letter
which the array size must be specified, then assign a value to it tolower(a_letter)
using a string-copy command, Returns a lower-cased a_letter
strcpy(girl, May);
1.2.7.2 Using a char pointer
However, before you can use the strcpy command, you need
to first include the string.h header file:
The second way to initialize a string is to use a char pointer
#include <string> variable:

5
char *boy = Peter; Do you know you can assign str1 to str2 and vice
versa?
A pointer variable is recognized by the asterix * sign. So the
variable boy is essential a pointer variable. However pointer is char str1[20];
the subject of Chapter 2. Here, you will be shown how you can use char *str2;
a char pointer to assign string values. strcpy(str1,"Hello");
str2 = str1;
char *boy; // Declare variable first
boy = Peter; // Assign value after declaration AND vice versa:

When using char pointers to assign string values, you do not char str1[20];
char *str2;
need to include the string.h header file. Moreover, you
str2 = Bye;
cannot use the strcpy command to assign a value to the strcpy(str1, str2);
variable in the same manner as a char array would.
Do you know you can strcat str2 with str1 but not
the other way around?
1.2.7.3 Examples
char str1[20];
boy deals with pointers, while girl deals with arrays, both of char *str2;
which we will cover in greater detail later. As for now, just strcpy(str1, Hello);
remember these two methods of assigning string values. str2 = World!;
strcat(str1, str2); // OK
Listing 1.2 strcat(str2, str1); // NOT OK!!

#include <iostream> Be patient. You will be told the answer of this mystery when we
#include <string> study pointers in Chapter 2.
using namespace std; // to simplify std::cout
// and other std:: functions 1.2.7.4 Using string
main() {
char str1[20]; C++ provides the string class library to handle a string of
char *str2;
strcpy(str1,"Hello"); characters elegantly. After including <string> header file, you
str2 = " World!"; can declare a string variable and use it with little fuss, because the
cout << str1 << str2; burden of memory ownership is transferred to the string class
return 0; rather than the programmer (i.e. you!).
}
Listing 1.3

The text Hello World! will be displayed on screen. #include <iostream>

6
#include <string>
using namespace std; char name[20];
cout << What is your name? \n;
main() { cin >> name;
string s;
s = Peter; The program will take in the first 19 characters. The 20th character
cout << "Hello " << s << "!" << endl;
return 0;
is reserved for the null character \0 that indicates the end of a
} string. As a user, you can enter a name that is longer than 19
characters, but only the first 19 characters will be stored in name.

The text Hello Peter! will be displayed on screen.


1.2.9 Loops
1.2.8 Acquiring Screen Input From User
1.2.9.1 for loop:
Let me introduce to you:
cin receives user input from the keyboard int x;
for (x = 0; x < 10; x++)
>> known as extractor operator where user input is stored in {
the variable defined after the operator. ...
}
Listing 1.4
Check this out:
#include <iostream>
using namespace std; int x;
char name[10];
main() for (x = 0; x < strlen(name); x++)
{ { ... }
int A,B;
cout << Enter a number : ; In reverse:
cin >> A;
cout << Enter another number : ;
for (x = 10; x > 0; x--)
cin >> B;
{ ... }
cout << The sum is << (A+B);
return 0;
} 1.2.9.2 while loop:

while (x < 10)


{
In the case where you want to limit the length of user input that is x++;
being read, you can first declare a character array to be of the cout << x << ;
maximum size allowed: }

7
Try this: 1.2.10.2 The switch case statement

while (++x < 10) If you have many choices, a more efficient code is to use the
{ switch case statement. Note that the switch case
cout << x << ; statement can be used to test integers and single characters only.
}
Let me introduce to you:
1.2.9.3 do ... while loop:
switch indicates which variable is to be tested
do case one of the test conditions.
{ break is used after the execution of the relevant codes when
cout << x++ << ; a particular condition is satisfied.
} while (x < 10); default is used as a last resort if none of the case statements
matches the variable that is being tested
1.2.9.4 infinite loops:
switch(a_character)
for (;;) { ... } {
case A: cout << A<< endl;
while(true) { ... } break;
case E: cout << E<< endl;
while(1) { ... } break;
case I: cout << I<< endl;
Note that the boolean value true is the value of 1. break;
case O: cout << O<< endl;
break;
case U: cout << U<< endl;
1.2.10 Making choices break;
default: cout << Not a vowel\n;
1.2.10.1 The if else statement }

if (age >= 18) If you do not write the break statement, the program will
{ continue to execute statements belonging to other cases. This is the
cout << You can vote << endl; fall through effect. Statements will be executed line by line
} until either a break statement or the end of the switch case
else
statement is encountered.
{
cout << Too young to vote << endl;
} switch(a_character)
{
case A:
case a: cout << A<< endl;
break;

8
case E: 1.2.11 goto labels
case e: cout << E<< endl;
break;
case I: The goto command is useful when you want your program to skip
case i': cout << I<< endl; from its current position to execute instructions on a certain line.
break; All you have to do is to label the line that you may want to skip to
case O: with later a label name. For example:
case o: cout << O<< endl;
break;
Listing 1.5
case U:
case u: cout << U<< endl;
break; #include <iostream>
default: cout << Not a vowel\n; #include <ctype.h>
} using namespace std;

main()
Alternatively, a more efficient way to safeguard from lower-case {
vowel inputs is to simply upper-case a_character with the float num1, num2;
command toupper that is provided by the string file: char choice;
cout << "Welcome!\n";
switch(toupper(a_character)) sum2numbers: // this is a label
{ cout << "\n\nEnter two numbers";
case A: cout << A<< endl; cout << " and I will add them up\n";
break; cin >> num1 >> num2;
... cout << "The sum is " << (num1 + num2);
} tryagain: // this is another label
cout << "\n\nDo you want to try again?";
When dealing with integers, the case conditions omit the single cout << "\n'Y'es or 'N'o\n";
quotes . cin >> choice;
if (toupper(choice) == 'Y')
{
switch(a_number) goto sum2numbers; // goto a label
{ }
case 1: // case 1:, not case 1: else if (toupper(choice) == 'N')
cout << Its a 1!; {
break; cout << "\nThank you!\n";
case 2: cout << Its a 2!; }
break; else
... {
} goto tryagain; // goto a label
}
return 0;
}

9
Chapter 2: Simple Pointers And Functions

Pointers are often confusing to programmers of all levels, value 9 that will be converted into binary format occupying 32
especially beginners. Usually programmers run away (screaming) bits, i.e., 0000000 00000000 0000000 00010001 is
when I ask them about pointers. Such extreme response has since inserted into this memory space.
served as a constant reminder for me to very cautiously pace
myself when I teach pointers. int x
32 bits (4 bytes) that carry the integer 9.

2.1 Introduction
0 0 ... 0 0 0 0 0 0 0 1 0 0 0 1
C++ gives the programmer (you) the power and flexibility to 1st byte of x
control the way the memory of the computer is utilized as being
instructed by your C++ program. In the olden days when RAM or This 32-bit memory space has an address so that the computer may locate the
memory is significantly more expensive, this task of coding variable x when required. For example:
efficient programs is of utmost importance. However, careful 0x0012FF7C.
memory management can be extremely tedious. Thankfully RAM The address points at the first byte of the memory space.
nowadays is much cheaper than before and we can be more relaxed
with memory management. However careless programming will
certainly result in disastrous consequences such as computer
hanging due to out of memory! 2.1.2 Understanding pointers

2.1.1 Understanding and managing memory Let me introduce to you


* (asterix) pointer
Lets try to understand what takes place in the context of the & (ampersand) 1) address of, or
memory of your computer when you enter the following 2) reference (depending on context)
instruction:
While you can declare and initialize an integer as in 2.1.1., you
int x = 9; may also choose to declare a pointer variable that points to that
integer. Pointing to actually means that the pointer variable is
The declaration int x instructs the computer to find and going to store the address of the integer, which is 0x0012FF7C,
allocate 32 unused bits (NB: An int data type is allocated 4 and not the integer value 9. A pointer variable is declared as
bytes which is 32 bits; a char 8 bits; and a double 64 bits). follows:
This memory space that is allocated to x has an address which
int *ptr_x;
points at the first byte of this space. Subsequently, the integer

10
Example of how it should be used: *ptr_x = 10;

int x = 9; (1) and not ptr_x = 10;! (Remember that ptr_x stores the
int *ptr_x; (2) address of x, and not the value x). You can however type
ptr_x = &x; (3)
std::cout << ptr_x;
Lets explain the codes above line by line.
Line Try it out for yourself and see what is displayed on the screen!
(1) x is declared as an integer (32-bits) and it is initialized Lastly, you should try the following codes to get a clearer picture
with a value of 9. Transparent to the programmer is the of what is happening:
allocation of 32 bits of memory space to x. The address
of this memory location is &x, read as the address of x. int x =9;
An example of &x is 0x0012FF7C. int *ptr_x = &x;
(2) An integer pointer variable ptr_x is declared. It will
cout << "Value" << "\t\t" << "Address\n";
store the address of an integer. cout << "x = " << x << "\t\t" << "&x = " <<
(3) ptr_x now stores the address of x, &x. In other &x << endl << *ptr_x = " << *ptr_x <<
words, ptr_x points to x. "\t" << "ptr_x=" << ptr_x << endl;

What you should remember NOT to do is: This is what you will get:

int *ptr_x = 10; // Wrong!

because it is actually the short-hand for the following codes:

int *ptr_x;
ptr_x = 10; // Wrong! 2.2 Arrays are Pointers?!?
which is wrong because ptr_x should have been the address of Yes, there is a subtle relationship between pointers and arrays. You
x, not the value of x. What you should do instead is to establish a might have noticed it when I introduced two methods of declaring
valid address of an integer first: a string: using a char array and using a char pointer.

int x = 9; The following instruction


int *ptr_x = &x; // Correct!
int arr[10] = {88,99,101};
Examples of accessing (retrieve or modify) x via its pointer
variable include: declares an integer array called arr of size 10 and initializes it
with 3 elements of type integer, leaving the remaining 7 elements
cout << *ptr_x; zero-initialized (yes, they are stored with 0s).

11
IMPORTANT: cout << *arr+1;
Typing arr by itself actually means you are retrieving the and
address of the array, and not the contents of the entire array! Try it cout << (*arr)+1;
if you do not believe me.
Because both pointer variables and array variables store
cout << arr; addresses, you can assign one array variable to another pointer
variable (assignment is done using the operator =):
will produce an address which looks something like 0x0000FF24.
This address marks the beginning of the array arr, which int arr[10] = {88,99,101};
int *ptr_arr;
coincidentally is also the address of the first element of the array
ptr_arr = arr;
arr, i.e., the integer 88.
Do not code ptr_arr = &arr; as in line (3) in Chapter 2.1.2
Since arr is an array, the contents of its elements can be because arr here is already an address. By now, I hope you
retrieved using the usual index system, where first element is wont be too surprised if I told you that
indexed 0:
int *arr;
cout << 1st element of arr is : << arr[0]; and
cout << \n2nd element of arr is : << arr[1]; int arr[];
...
are equivalent , as long as the array size is unknown. Yes they are!
Since arr is also an address, the contents at the address arr
can be retrieved using a pointer! Recall that *arr points to the Once initialized, the contents of an array can be modified
contents of arr, which turns out to be the first element of the and accessed via array index as well as pointers:
array. To select which element to retrieve, you simply add the
index to the address, arr, and prefix with the pointer, *, as cout << *(arr+1);
shown below: and
cout << arr[1];
cout << 1st element of arr is : << *arr;
cout << \n2nd element of arr is : << *(arr+1);
...
2.2.1 Using sizeof() to determine the number of
(arr + 1) is the address of the second integer element in array elements in an array
arr. In our example, (arr+1) would be the address 0x0000FF28
since arr is 0x0000FF24 (the address location is increased by 4 C++ has a built-in operator called sizeof that gives the sizes of
bytes because an integer occupies 4 bytes of memory space). data types in bytes. You can use it on both variables and data
types:
So what are the outcomes of the following codes? Go on, try it!
int x = 9;

12
cout << sizeof(x) << endl; cout << There are <<
// variable x: 4 bytes sizeof(arr)/sizeof(arr[0]) << elements.;
cout << sizeof(int) << endl; 2.2.2 Answer to Question in Chapter 1.2.7
// data type int: 4
Iin Chapter 1.2.7, you were asked why can you only strcat
Now it can be used to determine the number of elements in an
array. If you can correctly guess the output of the following codes, str2 with str1 but not the other way around?
then I am sure you will have no problems understanding how to
char str1[20];
determine the array size: char *str2;
strcpy(str1, Hello);
double arr[10]; str2 = World!;
double *ptr_arr = arr; strcat(str1, str2); // OK
cout << sizeof(*arr) << endl; // 8 strcat(str2, str1); // NOT OK!!
cout << sizeof(arr) << endl; // ? (1)
cout << sizeof(ptr_arr) << endl; // (2)
ANS: str1 is an array with pre-allocated memory space large
In line (1), arr is used to represent the entire array although it enough to store 20 characters. str2 on the other hand is
also holds the address of the array. Therefore sizeof(arr) declared as a pointer variable and has just enough space to
will return 80 (bytes) since a double occupies 8 bytes of space store the word World!. Appending World!
and there are 10 of them. behind Hello is OK because of the extra space str1
has, but appending to str2 is disallowed because str2
In line (2), ptr_arr being a pointer variable, does not has just enough space for itself.
actually know the size of the entire array it is pointer to. Its content
is the address of arr which occupies 4 bytes. Therefore, rather 2.3 Functions
surprisingly, line (2) yields 4 and not 80.

Thus, to determine the number of elements in an array, write: 2.3.1 What is a C++ function?

cout << There are << In the context of C++, a function is a couple of lines of C++
sizeof(arr)/sizeof(int) << elements.; instructions bundles together under one unique name. For example,
using a layman example, a series of instructions:
Since all arrays will at least have one element, you can also use the
following codes: Fry fish
Steam chicken
cout << There are << Stir fry vegetables
sizeof(arr)/sizeof(*arr) << elements.; Boil soup
Serve with rice
or equivalently

13
can be bundled under the function name PrepareMeal. Thus ans = ans * count;
when you want a meal prepared, you only need to instruct the }
kitchen to PrepareMeal instead of giving 5 sets of instructions.
It saves even more time if you PrepareMeal everyday! 2.3.4 The anatomy of a function
One way of writing the factorial function is shown below.

2.3.2 Why write C++ functions? int factorial(int n) {


int ans = n;
Functions are like small modules. You can code a function, test for for (int count = n 1; count>=1; count--)
it, remove any bugs, and then it can be used over and over again. {
Moreover, software engineers often work in a team, where each ans = ans * count;
}
member can be assigned a particular module to work on. Once return ans;
functions are developed by various programmers, the complete }
program may be easily and seamlessly combined to yield the final
software. And now a step by step guide on how to write a function.

1. CHOOSE A UNIQUE FUNCTION NAME


2.3.3 When to write functions
int factorial(int n) { ... }
A function should be written if a program needs to repeatedly
Think of a unique name for your function as long as it is not a
perform a task or several similar tasks.
reserved keyword in C++ (like while, include, bool, etc).
Say your math lecturer wanted his class to calculate 7! + 9!, where It is good practice to name the function as descriptively meaningful
n! = n(n 1)(n 2) (3)(2)(1). While most students would take and as close to its intended task as possible. Thus, I believe it is not
out a pen and a piece of paper, you as a proud C++ programmer surprising that I have named the function factorial.
reach out into your bag and pulls out a laptop instead. When 2. DETERMINE ALL ITS INPUT FORMAL PARAMETERS
Windows is started, you immediately double-clicked MS Visual
int factorial(int n) { ... }
C++. You probably noticed 7! + 9! involves:
The task that a function performs usually depends on a certain
1. A repeating task (factorial is performed twice)
inputs. A factorial function would compute the factorial of n, n
2. A recognizable pattern in that task (i.e., factorial):
being the input. To be correct, n is called the input formal
// Pattern to compute n! :
parameter. Note the following when writing a function:
int ans = n;
for (int count = n 1; count>=1; count--) Place input parameter in parentheses, ( ), behind the
{ function-name.

14
int factorial( <input parameter> ) If there is no return value, you must indicate void.

Specify the data type of input parameter. void factorial(int n) { ... }

int factorial(int n) 4. PLACE INSTRUCTIONS IN FUNCTION BODY

Multiple input parameters are allowed. All parameters are int factorial(int n)
separated by commas. {
int ans = n;
int factorial(int n, float m, char *c) for (int count = n1; count>=1; count--)
{
ans = ans * count;
If no input is required, either }
leave blank within parenthesis (), or return ans;
write the word void in parenthesis, (void). }

int factorial() 5. OPTIONAL WRITE A FUNCTION PROTOTYPE


OR
factorial(void) A function prototype is just a single line of code that states the
inputs and output of a function. It performs no action other than to
Input can have a predefined default value which will be warn the compiler of what to expect later in the function definition.
used in case no input value is provided by the program. Prototypes are optional but are recommended so that correct type
checking for arguments (i.e., values, constants, pointers, etc) and
int factorial(int n = 2) return values is enabled. In other words, it is used to check if the
number of arguments and their data types in the calling function
New variables are declared according to the input parameter correspond to the number of parameters and their types in the
list. However, they only exist within the local scope of the called function when the function is called. A prototype contains
function. They will be destroyed at the end of the function. the header of a function without the detailed instructions found in
the function body.
I recommend writing a function prototype before the start
3. DETERMINE ITS RETURN TYPE of the main program. They are also usually written in a separate
header .h file.
int factorial(int n) { ... }
A function can return at most one value back to the program, Example of the factorial function prototype (remember semicolon):
sometimes none. You need to determine the data type of the value
that is being returned. Write this data type in front of the function. int factorial (int n=2);
In factorial, the obvious return value is the resulting factorial,
n!, which is an integer.

15
2.3.5 Using functions to simplify complex tasks int result1, result2, result3, result4;
result1 = factorial(5,1);
result2 = factorial(result1,2);
An example to show why using functions can really simplify result3 = factorial(result2,3);
complex tasks: result4 = factorial(result3,4);
cout << The answer is << result4;
Lets say your math lecturer for no reason turned evil (forgive me, return(0);
Mr Koh!) and assigned you a really tough question: }

Find {[(5! + 1)! + 2]! + 3}! + 4.


2.3.6 Nested functions
Here a repeating task is observed. The pattern is not merely a
simple factorial of n!, but it the the sum of this factorial and Experienced programmers will find the above program in Listing
another integer. The resulting pattern is therefore n!+m. The codes 2.1 a little long-winded. They prefer to write nested functions to
to compute the above expression is given in Listing 2.1. For the reduce usage of unnecessary variables which may confuse readers.
sake of simplicity, I will not rename this new function that The following program uses nested functions in a way where the
computes n!+m, but instead reuse the name factorial. operation is identical to the previous program.

main()
Listing 2.1 factorial function {
int result;
#include <iostream.h> result = factorial( factorial(
factorial( factorial(5,1),
// Function prototype 2) ,3) ,4);
int factorial (int n, int m=0); cout << The answer is << result;
return(0);
// Function body }
int factorial (int n, int m=0)
{ Nested functions are functions whose input values are determined
for (int count=n-1; count>=1; count--) by the output of other functions. One condition is for the data types
{ of the input and corresponding output to be similar. Recall that the
n = n * count; factorial function returns an integer which can in turn be
} passed as an argument (input) to another factorial function.
return (n + m);
}

// main() body Program entry point 2.3.7 Recursive functions


// Calculate {[(5! + 1)! + 2]! + 3}! + 4
main() The function itself can be further simplified via recursion. A
{ recursive function actually calls itself, passing input values and

16
expecting a return value at the same time. Recursive functions
must have a termination condition to prevent it from calling itself main()
without end. {
int buddy_age = 20;
// original age (as of yesterday)
The factorial function can also be written via recursion: makeWish(buddy_age);
cout << \nYou are now << (buddy_age+1);
int factorial (int n) // buddy_age is 20. Still not updated
{ cout << years old!\n;
// Termination condition }
if (n<=1) return 1;
// Recursion
else n = n * factorial(n-1);
return n; If I wanted to change his age within the function, by writing
}
void makeWish(int age) {
cout << Happy << age << st birthday!;
age++;
2.3.8 Pass by value and pass by reference }

Now that you can write functions and pass appropriate arguments will not affect buddy_age in the main() program because
(values, constants, pointers, etc) in and out of them with ease, let age is simply a copy of buddy_age that exists only within the
me shift your focus slightly to another pressing issue surrounding scope of the function. The solution is to pass the buddy_age
functions the manner in which the arguments are passed (which argument by reference. This means instead of making a copy of
may alter the value of the original variable). buddy_age called age, you declare a reference variable age
which contains the address of buddy_age so that age is
Lets say you want to wish your buddy Happy 21st effectively pointing to buddy_age. Consequently, any changes
birthday! and then say You are 21 years old to age will effectively change its referenced value, i.e.,
now!. Well, as of yesterday, he was still 20 years old and that is buddy_age.
what your computer system would show of about his age.
To pass an argument by reference, just append the reference
Listing 2.2 Pass by Value operator, &, to the identifier of the input parameter:
#include <iostream>
using namespace std;

void makeWish(int age); Listing 2.3 Pass by Reference

void makeWish(int age) { #include <iostream>


cout << Happy << age << st birthday!; using namespace std;
}

17
void makeWish(int& age) { function. Fortunately, you can prevent this from happening by
cout << Happy << age << st birthday!; using the const keyword which makes a referenced argument
age++; read-only. Lets say you are writing a function that squares an
}
integer number:
main()
{ int square(const int& number)
int buddy_age = 20; {
// original age (as of yesterday) return number*number;
makeWish(buddy_age); }
cout << \nYou are now << buddy_age;
// buddy_age is now 21! Notice the reference to number is now forced to be constant,
cout << years old!\n; which means number is a reference to a read-only argument.
} Compiler will check for unwanted changes to number and
would alarm with a compilation error if it spots any modification
to number. Example:
Since age is a reference to buddy_age, buddy_age is
incremented when age++; is executed even though age is int square(const int& number)
WITHIN the function scope {
number *= number;
There is another excellent reason why you should pass // illegal! number is read-only!
arguments by reference instead of passing by value:- which is return number;
when the object being passed is simply too big! Actually objects }
are almost never too big it is simply inefficient to be duplicated
within a function. Say you need to pass an object which is 100-
bytes big into a function. Therefore, wouldnt it be more efficient 2.3.10 Passing arrays
had you only passed the address of the object, which occupies only
4-bytes, into the function? Arrays are always passed as pointers in function calls. The
following example passes an array into a function and returns the
same array except that it now points to the second element.
Pointing to the second element simply means that the returned
pointer variable contains the address of the second element of the
array.
2.3.9 Pass arguments by constant reference
Listing 2.4 Passing arrays
OK, now there is a conflicting issue. What if the object to be #include <iostream>
passed is large but you are worried that by passing by reference, using namespace std;
the object may be modified (intentionally or otherwise) within the

18
int* nextElement(int arr[5]) or a pointer variable which stores an address as shown below (if
{ you do not know the array size, use an empty array or a pointer
return ++arr; remember an empty array is equivalent to a pointer variable see
}
Chapter 2.2):
main()
int* nextElement(int *arr)
{
{
int x[5]={7,14,21,28,35};
return ++arr;
cout << \nAddr of array x: << x;
}
cout << \n1st element of x: << x[0];
cout << \nAddr of 1st element: << &x[0];
OR
cout << \n2nd element of x:
<< *nextElement(x); int* nextElement(int arr[])
cout << \nAddr of 2nd element: {
<< nextElement(x); return ++arr;
cout << \n1st element of x: ; }
cout << *( nextElement(x) 1 ) << endl;
return 0; On the other hand, you write
}
cout << x[0];

Since it is essentially an address that is being manipulated in the screen displays the contents of the first element in x, i.e., 7. If it is
function, incrementing the address by 1 simply points to the x[0] that you are passing, then the input parameter is simply an
second element of the array. The address of the second element is integer, not a pointer.
returned via a pointer variable.

CONFUSED ABOUT PASSING ARRAYS?? 2.3.11 Function Overloading


The essence of passing arrays correctly is to realize that the array
x is passed as an address, and not the contents of the entire array. Traditionally, a function performs a specific duty that is
programmed for it. C++ supports function overloading, adding
If ever in doubt what in the world you are passing, simply test the
extra duties to existing functions. This is done simply by defining
contents of your argument by printing it out on screen:
multiple versions of a function with the same name, but the
cout << x; versions must have different signatures. Lets say you want to
write a function which returns the larger value of two inputs.
In this case, the screen displays an address which is the address of However, you do not know what the data type of your inputs is
x. Since an address is passed, the input parameter of the function, they can be either integers or doubles. Depending on the data type
nextElement must be either an array of x as in Listing 2.4, of the inputs, you have to return a similarly typed value. So you
write the following for two integer inputs, x and y.

19
double maxi(int x, int y)
int maxi(int x, int y) {
{ return (x>y ? x : y);
return (x>y ? x : y); }
/* What it means: (test if x>y ) ?
(if x>y, return x) : (else, return y) */ Compiler will complain of illegal overloading because overloaded
} function differs only by its return type.
You are allowed to overload the maxi function by writing yet Case 2:
another function with the same name, but of a different signature. When default values are provided for certain input parameters
A signature of a function is constituted by the number, the order,
and the data types of its input formal parameter. Note that different If the input parameters of a function were provided with default
return types do not change a function signature. The key to values, then the function has more than one signature.
overloading functions properly is to have distinguishable
signatures. Thus, an overloaded maxi function is: int power(int x, int y=2)
{
double maxi(double x, double y) int total=1;
{ while(y-- >= 1)
return (x>y ? x : y); total *= x;
} return total;
}
The signature of the first average function consists of two input The above code computes x to the power of y. However, because
parameters whose types are both integer. The signature of the a default value of y is provided, the function works with only a
overloading function consists of also two input parameters but value of x being provided or both x and y provided.
their data types are different double. Therefore it is legal. Therefore, it now has two signatures. Thus

int power(int V=5, int I=1, int R=100)


Illegal overloading is a common mistake and somewhat confusing {
at times. Below are examples of three common mistakes made by // Power = VI = I2R = V2/R
return (V*I);
programmers when attempting to overload a function:
}
would present the issue of conflicting signatures because it now
has FOUR different signatures of three, two, one or zero integer
Case 1:
inputs associated to it. Two of its signatures conflict with the
A different return type does NOT change a function signature
signatures of the first power function of two and one integer
int maxi(int x, int y) inputs.
{
return (x>y ? x : y);
}

20
Case 3: {
When passing arrays and pointers char number_plate[10];
int year_of_manufacture;
float engine_cc;
Since arrays are always passed as pointers in function calls, input
bool transmission;
parameters like int *ptr and int arr[] are not // true for auto, false for manual
distinguishable as far as the signature is concerned. The following };
is the example taken from Listing 2.4.
To declare a structure, write the following code:
int* nextElement(int *ptr)
{ struct car mycar1;
return ++ptr; struct car mycar2;
} struct car dads_car1;
int* nextElement(int arr[])
{
return ++arr; 2.4.3 Manipulation of the elements of a structure
}
The data is manipulated by using the DOT notation:
Compiler will complain that nextElement already has a body,
which is another way of saying conflicting signatures. // ==== Modification ====
strcpy( mycar1.number_plate, WKW 8888 );
mycar1.engine_cc = 1798;
mycar1.transmission = FALSE; // manual
2.4 Structures mycar1.year_of_manufacture = 2003;
// ==== Retrieval ====
cout << mycar1.number_plate;
2.4.1 Why use structures? cout << mycar1.year_of_manufacture;
Sometimes I wish that I can declare just one variable but it bundles
together information of various data types. Structure is made for 2.4.4 Array of structures
this purpose.
So you have 5 cars. Use an array then:
2.4.2 Anatomy of a structure
struct car mycars[5];
Consider a new data type called car which has the following strcpy(mycars[1].number_plate, BBB 8888);
attributes: mycars[3].year_of_manufacture = 1999;

struct car

21
Chapter 3 : Classes, The Basis Of Object-Oriented-Programming

3.1 Introduction to classes 1) The keyword struct is replaced with class


(obviously!). E.g., class Complex.
Object oriented programming (OOP) revolves around classes. 2) A class is divided into 3 sections, called public:,
What are classes? Well, from a class, you create objects (which is private: and protected:. All members of a structure
the objective of object-oriented-programming, right?). So what are are inherently public!
objects? There is no simpler way of putting it than to tell you that 3) A class may have functions too (which include constructors
and destructors)!!
int x; There are several other differences but they are too advanced
for now. Details in later chapters.
creates an object x from the class int! So x is an (integer)
object. Hope I didnt shock you. Well, you are absolutely correct if In our Complex class, there should be a real and an imaginary
you say that x is a variable of data type integer. Under OOP, x part, both of which are typed double. It should also consist of a
is essentially an object created from the class int which is a function called showNumber which when called will display on
class prewritten and provided by C++. And from one single class, screen the typical form of a complex number, i.e., x+yi. Yes, an
you can create as many objects as you like: object not only has attributes like the real and imaginary values, it
can have functions too! In addition, I will throw in another
int y; function called magnitude which calculates and returns the
int z; magnitude of this complex number, where its formula is given by
int x_arr[10];
x2 y2 .
Of course, int is just a simple class provided by C++. You can
create your own classes... perhaps something more useful like a Lets write a class which we can declare a complex number object
complex number class which is the focus of this chapter:
Complex c_num;

3.2 Lets write a class and assign real and imaginary values using the DOT notation

Lets write a class called Complex. Objects created from this c_num.real=2;
class can store and manipulate a complex number. Writing a class c_num.imag=5;
is very much like writing a structure (see Chapter 2.4). There are a
few distinct differences though: and call member functions

c_num.showNumber();

22
cout << endl << c_num.magnitude(); double Complex::magnitude()
{
which display on screen return (sqrt(real*real + imag*imag));
}
2+5i
5.38516 // Constructor of class Complex
Complex::Complex()
{
OK, now the codes for the Complex class: real = 0;
imag = 0;
Listing 3.1 Complex class cout << "========CONSTRUCTION========\n";
cout << "I'm being created! Yeah!\n";
#include <iostream> cout << "My new value is ";
#include <math.h> showNumber();
using namespace std; cout << "\n============================\n";
}
class Complex
{ // Destructor of class Complex
public: Complex::~Complex()
// Data members {
double real, imag; cout << "\n========DESTRUCTION========\n";
cout << "I'm being destroyed! Sob...\n";
// Member function prototypes cout << "My last value is ";
void showNumber(); showNumber();
double magnitude(); cout << "\n============================\n";
}
// Constructor prototype
Complex(); main()
{
// Destructor prototype Complex c_num;
~Complex();
cout << "\nAssigning new value : ";
private: c_num.real=2;
c_num.imag=5;
protected: c_num.showNumber();
}; // Note semicolon
cout << "\nIts magnitude is : ";
// ==== Member functions of Complex ==== cout << c_num.magnitude() << endl;
void Complex::showNumber(void) return(0);
{ }
cout << real << "+" << imag << "i";
}

23
3.2.1 Screen Output Under public:, there are

two double variables called real and imag intended for


storing the real and imaginary parts of a complex number
member function prototypes for showNumber and
magnitude
the prototypes of constructors and destructors. A constructor is
like a function that is automatically called when a Complex
object is created. Constructors carry the same name as the
class, i.e., Complex(). You can overload the constructor.
When no constructor is defined, C++ automatically constructs
a default and empty constructor for you. A destructor is a
function that is called when the object is destroyed, either
3.2.2 Explanation automatically at the end of the program, at the end of function,
or explicitly by the programmer. It has the same name as the
The name of the class is Complex. Any object that is to class, with a tilda ~ sign added in front, i.e.,
be created from this class must use this name. E.g., ~Complex().
Complex c_num; Lets go through the main() program:
Do not make the mistake of writing Complex c_num;
Complex c_num(); // Wrong!
declares an object from the class Complex. This line of code
The body of the class is enclosed within a pair of open instructs C++ compiler to allocate sufficient memory for this
and close bracers, terminated with a semicolon. Within the body, object. In addition, the constructor (Complex::Complex()) is
there are three main sections, i.e., public:, private: and called when this line is executed, initializing real and imag
protected:. The data members and member functions of the to zero. That explains why you will see the first 4 lines of the
class are written here. You place the members under different screen display output.
sections when you want them to have different access restrictions.
In short, members written under the private: can only be
accessed by the host object only, i.e., c_num only (not even
objects created from the same class, e.g., c_num2, c_num3),
while members written under the public: section can be You can use the dot notation to access public data members
accessed by everybody (anywhere in the program). For now, just and member functions of c_num:
write every member under public since it has the widest access.
c_num.real=2;

24
c_num.imag=5; The following program demonstrates the usefulness of a static data
and member. It is similar to Listing 3.1 except with the inclusion of the
tatic cnum_count has been included:
c_num.showNumber(); // Displays 2+5i
cout << c_num.magnitude() << endl;
// Calculates magnitude of 2+5i and displays
Listing 3.2 Complex class with static members
Finally, when the code return(0); is executed, C++
#include <iostream>
automatically destroys all objects created earlier. Before c_num #include <math.h>
is destroyed, the destructor function, Complex::~Complex(), using namespace std;
is first called. This is why you get the final screen output.
class Complex
{
public:
double real, imag;
static int cnum_count;
Complex();
~Complex();
3.3 Static Members };

int Complex::cnum_count = 0;
Sometimes there might be certain information that you would want
to retain throughout the program. For example, you may want to Complex::Complex()
keep count of the total number of existing complex number objects {
cnum_count++;
in your program. In such a case, you need to declare the integer // At object creation, cnum_count is incremented
static within the class. For example: real = 0;
imag = 0;
class Complex cout << There is(are) now <<
{ cnum_count << complex number(s).\n;
public: }
double real, imag;
static int cnum_count; Complex::~Complex()
... {
}; cnum_count--;
// At object destruction, cnum_count is
/* // decremented
Static member initialization is written straight cout << cnum_count;
after class {}; Dont forget to write Complex:: cout << complex number(s) left.\n;
*/ }
int Complex::cnum_count = 0;

25
main()
3.4 A Better Class
{
/* There are two ways of accessing static Now that you have understood the basic elements involved in
members: via object, and via class writing a class, lets learn how to write better classes. I will show
*/ you an improved version of the classes written in Listing 3.1 and
// via class Listing 3.2. The basic changes here in Listing 3.3 include
cout << Complex::cnum_count; (1) 1. hiding the data members real, imag and cnum_count,
cout << complex number(s).\n;
and then
Complex c_num1; 2. writing suitable public functions to access the hidden data
Complex c_num2; members, as well as
3. overloading constructors to make initialization of a Complex
// via objects object more convenient and efficient.
cout << c_num1.cnum_count; (2)
cout << complex number(s).\n; Listing 3.3 Complex class
cout << c_num2.cnum_count; (3)
cout << complex number(s).\n;
#include <iostream>
#include <math.h>
return(0);
using namespace std;
}
class Complex
{
The output is shown below. The first line of the output is a result public:
of (1) value retrieval of cnum_count via its class. There is
no other way of access because no complex objects exist at that // Member function prototypes
void showNumber();
moment. Later, two identical display lines 2 complex double magnitude();
number(s) are the result of lines (2) and (3). Actually, // Private data accessor functions
they all point to the same address regardless of whether access is void setValue(double real, double imag);
via its class or any one of the existing objects. double getReal();
double getImag();
static int getCount(); // Get cnum_count

// Constructor prototypes
Complex();
Complex(double r);
Complex(double r, double i);

// Destructor prototype
~Complex();

26
private: double Complex::magnitude()
{
// Data members return (sqrt(real*real + imag*imag));
double real; }
double imag;
static int cnum_count; // Constructors of class Complex
Complex::Complex()
}; // Note semicolon {
real = 0; imag = 0;
// ==== Static members ==== cout << "Complex object (0+0i) created.\n;
int Complex::cnum_count = 0; cout << "There are " << ++cnum_count;
cout << " Complex objects.\n\n";
int Complex::getCount(void) }
{
return cnum_count; Complex::Complex(double r)
} {
real = r; imag = 0;
// ==== Member functions ==== cout << "Complex object (";
void Complex::setValue(double r, double i) showNumber();
{ cout << ") created.\nThere are ";
real = r; cout << ++cnum_count << " Complex
imag = i; objects.\n\n";
cout << "Value set :"; }
showNumber();
cout << "\n\n"; Complex::Complex(double r, double i)
} :real(r),imag(i)
{ // More efficient way of initialization
double Complex::getReal() cout << "Complex object (";
{ showNumber();
return real; cout << ") created.\nThere are ";
} cout << ++cnum_count << " Complex
objects.\n\n";
double Complex::getImag() }
{
return imag; // Destructor of class Complex
} Complex::~Complex()
{
void Complex::showNumber(void) cnum_count--;
{ cout << "Complex object being destroyed\n";
cout << real << "+" << imag << "i"; cout << cnum_count << " Complex object(s)
} left.\n\n";

27
} other objects created from the same class can
main() access private members.
{ protected: Members can be accessed by itself and its
cout << "\nProgram begins.\n";
subclasses (classes that inherit this class).
cout << "There are " << Complex::getCount();
cout << " Complex objects.\n\n\n";
Complex c_num1; It is good practice to hide sensitive data members (place them
c_num1.setValue(2,5); under the private section) so the (possibly malicious) public cannot
Complex c_num2(7,8); have direct access to them. For example, you do not want anybody
return(0); to alter the Complex object count, cnum_count. Therefore
} you should declare it private. The only way you can access it is via
the public function, getCount, which by the way does not
modify its value. The only time the value change is in the
Screen output: constructor and destructor, suitably so.

private:
double real, imag;

You then write public functions to access these private data


members. For example,

public:
void setValue(double real, double imag);
double getReal();
double getImag();

Protected members will come in handy when we learn about


inheritance in later chapters.

3.3.1 public, private and protected 3.4.2 Constructors


A brief summary of member access restrictions of each section: You can overload a constructor as shown:
public: Members can be accessed from anywhere of the
entire program, i.e., by everybody. Complex::Complex(double r, double i)
private: Members can be accessed by member functions {
in the same class. In other words, members can real = r;
imag = i;
be accessed by the host object only. Not even
}

28
according to class declaration, then imag
and you can conveniently declare and initialize a complex number: */
}
Complex c_num(2,5); // giving 2+5i Thus, it is good practice to try to keep the order of both the
declaration and the initialization list the same to avoid confusion.
What I showed you earlier in Listing 3.3 is a more efficient way to
initialize. This is called the init-list method because the constructor Further, you need to initialize if:
header is followed by a list of data members and their respective No default constructor is defined but there other constructors
initial value: that expect input values
You have a data member which is a reference. Recall that a
Complex::Complex(double r, double i) reference is a name for an existing object. Therefore, it should
:real(r),imag(i) be initialized or else it will be left pointing to a temporary
{ value.
}
class Complex
You can also insert default values: {
private:
Complex::Complex(double r, double i=0) Name& c_name;
:real(r),imag(i) // ...
{ };
}
Complex::Complex(Name& n):c_name(n)
Although initialization can be achieved with or without using the {
init-list, they are not equivalent in general. For initialization // ...
purposes, the init-list is the right choice. }

Note: Data members are initialized in the order they are listed in You have a data member which is declared const but has
the class declaration, not the order in the initialization lis: no value yet.

class Complex class Complex


{ {
private: private:
double real, imag; const int max_count;
// class declaration: real first, then imag // ...
public: };
Complex(double r, double i):imag(i),real(r)
{}; Complex::Complex():max_count(99)
/* {
Initialization list begins with imag then // ...
real. However real gets initialized first }

29
{
cout << real << (imag<0 ? "-" : "+")
<< (imag<0 ? imag*-1 : imag) << "i";
3.4.3 Destructors }

Destructor functions are called when the object is being destroyed


or deleted from the computers memory. For example at the end of
the program when return 0; is executed. The purpose of the
3.5 Can I Have Pointers For Objects Too?
destructor is to allow the termination of an object in an orderly
fashion. It is declared in the same fashion as the constructor except YES! There...
that it is further prefixed by the tilda sign, ~. Its prototype and
Complex c_num(4,5);
member function typically look like this:
Complex *p = &c_num;
~Complex(); // Prototype

Complex::~Complex() // Destructor function 3.5.1 Accessing Members


{
cout << Thats all folks!\n; And this is how you access the members of c_num via its
} pointer:

Even though you may survive without a destructor, good p->showNumber();


programming practice encourages destructors be written. It is (*p).showNumber();
because when an object is created, the amount of available
memory decreases. Therefore when the object is destroyed, the They are both equivalent. C++ provides the member accessor
destructor should release the memory appropriately. Failure to do operator -> for pointers so that you do not have to type * in
so will result in memory leak. order to retrieve the contents of c_num. Check out the following
example program that calculate the distance between two complex
numbers:

3.4.4 A small task Listing 3.4 Complex class with pointers

By the way, for the sake of beautifying the output as good


// adopting the codes from Listing 3.3
programmers like to do, can you rewrite the showNumber()
function so that for negative imaginary numbers, instead of main()
displaying, say, 5+-2i, it will display 5-2i? {
Complex c_num1, c_num2;
Sample Solution (give it a try first before peeking at answer): Complex *p1, *p2;
void Complex::showNumber(void) p1 = &c_num1;
p2 = &c_num2;

30
p1->setValue(1,1);
p2->setValue(4,5); main()
cout << "Distance is "; {
cout << c_num1.calcDist(p1, p2); Complex c_arr[2];
return 0; Complex c_num1(1,1), c_num2(4,5);
} c_arr[0] = c_num1;
c_arr[1] = c_num2;
double Complex::calcDist(Complex *c1, Complex cout << "Distance is ";
*c2) cout << c_num1.calcDist(c_arr);
{ // An address is passed into calcDist
double x, y; return 0;
x = c1->getReal() - c2->getReal(); }
y = c1->getImag() - c2->getImag();
return sqrt(x*x + y*y); double Complex::calcDist(Complex *c_arr)
} {
Complex c_dist( (*c_arr).getReal()
(*(c_arr+1)).getReal() ,
(*c_arr).getImag()
(*(c_arr+1)).getImag() );
3.5.2 Object Arrays
Complex *c_ptr = &c_dist;
Remember that an array is essential a pointer too (because an array
is represented by an address, just like pointers)! Let me show you return c_ptr->magnitude();
one way of finding the distance between two complex numbers }
which are now stored in an array. Remember that this is just one of
many ways to get the job done!

31

Vous aimerez peut-être aussi