Você está na página 1de 35

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

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

#include <iostream> int main()


{
int main() std::cout << Hello world! << std::endl;
{ return 0;
// Your fantastic program here }
return 0;
} To make this program work,
1. Type the codes shown above.
If youve noticed, a typical C++ program would have 2. Save the code to disk. You should always do this. The file
1. #include < > extension should be .cpp (c-plus-plus).
2. int main() 3. Compile the program.
(alternatively just write main(), its the same!) 4. Build the program.
3. open bracers, { 5. Run the program.
C = A + B;
The console will display Hello world! // Subtract
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
int integers, i.e., ,-3,-2, -1, 0, 1, 2, 3, C = A % B; // 5 / 2 = 2 remainder 1
float they are real numbers which may contain decimal points. // Thus, C = remainder = 1
double like float, they are real numbers but with more
precision. The divide statement will not produce 1.5 as you would expect.
char character, they include keyboard entries. Instead, it gives 1. Do you know why?
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
You need to declare all variables that you intend to use with a data whole bit) of the result of the division and ignores the mantissa
type. The variable must have a unique name. Remember that C++ (the decimal bit).
is case sensitive, so the variable number is not the same as
NuMBeR. You can initialize the variable (i.e., to assign an initial First solution to get C = A / B = 1.5 is to declare all A, B, and
value to the variable) either during declaration: C as floats. Even if C is a float but A and B are integers, you will
int A = 10; still be getting C = 1!
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;

1.2.4 Simple arithmetic 1.2.5 Useful shorthands for simple arithmetic


int A; A++; is shorthand for (s.h.) A= A + 1;
int B, C; ++A; s.h. A= A + 1;
A--; s.h. A= A 1;
A = 5;
--A; s.h. A= A 1;
B = 2;
A += B; s.h. A= A + B;
// Add
A -= B; s.h. A=A B;
A *= B; s.h. A = A * B;
A /= B; s.h. A = A / B;

A += B++; s.h. A = A + B;
1.2.7 String of Characters
B = B + 1;
Use char to initialize a single character:
A += ++B; s.h. B = B + 1;
A = A + B; char a_letter = A;

1.2.6 Arrays and a string of characters well, there are two ways of doing so:

1.2.7.1 Using a char array


An array is composed of consecutive memory cells that are
allocated to store data of a certain type. The array char girl[10] = Kelly;
int m[5];
Note single quotes for single characters and double quotes
for string-of-characters (strings).
declares m as an array and defines for it 5 cells (memory blocks)
where each can be accessed as m[0], m[1], , m[4]. Each cell An array of characters is declared and its elements are initialized as
is large enough to hold a quantity of type int, which is exactly 4-
bytes. Initialization can be done on a per-cell basis : girl[0] = K
girl[1] = e
m[0] = 2; girl[2] = l
m[1] = 3; girl[3] = l
m[2] = 5; girl[4] = y
m[3] = 7; girl[5] = \0.
m[4] = 11;
The last character is known as the null character. Its usage is to
or all at once at declaration: inform the C++ compiler that the string will be terminated at this
point. Thus, do not forget to allocate space for this character. This
int m[] = {2,3,5,7,11};
is equivalent to:
When the array size is left unspecified, the C++ compiler figures
out the dimension of m from the number of data items supplied. char girl[10] =
You can pre-allocate more memory than you would initially {K, e, l, l, y, \0};
require:
This is also an example of an over-sized array where the girl array
int arr[10] = {1,2,3,4}; is set to be of size 10 but only 6 characters are actually required.
This is perfectly alright because the null character is present to
The size of arr is 10. It is initialized with only 4 elements for now.
inform the C++ compiler that the string ends there. If for any
The remaining memory slots can be assigned some values later.
reason you would like to add characters behind Kelly, you need to Reads as string concatenate (add). Appends , Jill to the
shift the null character to the end of the string. For example, if you original value Jac. Thus girl becomes Jac, Jill.
want to change girl to Kelly Tan. strcmp(girl, May) // No semicolon
Reads as string compare.
girl[5] = ; // Replace \0 with space Returns 1 if the name matches May and 0 if not.
girl[6] = T; if (strcmp(girl, May) == 1)
girl[7] = a; cout << Hi May!\n;
girl[8] = n;
girl[9] = \0;. strlen(girl) // No semicolon
Returns the length of the string
In addition, you also do not have to pre-determine the size of the int len = strlen(girl);
string array. You can leave out its size and the C++ compiler will
automatically figure that out! There are other useful character manipulation functions for single
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
declare the variable, If you do include this header file, you can use the following
commands:
char girl[10];
toupper(a_letter)
which the array size must be specified, then assign a value to it Returns an upper-cased a_letter
using a string-copy command, tolower(a_letter)
Returns a lower-cased a_letter
strcpy(girl, May);

However, before you can use the strcpy command, you need to 1.2.7.2 Using a char pointer
first include the string.h header file:
The second way to initialize a string is to use a char pointer
#include <string>
variable:
If you include string, you can also make use of several helpful
char *boy = Peter;
string related functions such as
A pointer variable is recognized by the asterix * sign. So the
strcpy(girl, Jac);
variable boy is essential a pointer variable. However pointer is the
Reads as string copy.
subject of Chapter 2. Here, you will be shown how you can use a
Copies the string Jac into the variable, name.
char pointer to assign string values.
Command will overwrite previous values, if any.
strcat(girl, , Jill);
char *boy; // Declare variable first
boy = Peter; // Assign value after declaration
char str1[20];
When using char pointers to assign string values, you do not need char *str2;
to include the string.h header file. Moreover, you cannot use the str2 = Bye;
strcpy(str1, str2);
strcpy command to assign a value to the 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];
char *str2;
boy deals with pointers, while girl deals with arrays, both of strcpy(str1, Hello);
which we will cover in greater detail later. As for now, just str2 = World!;
remember these two methods of assigning string values. strcat(str1, str2); // OK
strcat(str2, str1); // NOT OK!!
Listing 1.2
Be patient. You will be told the answer of this mystery when we
#include <iostream> study pointers in Chapter 2.
#include <string>
using namespace std; // to simplify std::cout 1.2.7.4 Using string
// and other std:: functions
main() {
char str1[20]; C++ provides the string class library to handle a string of
char *str2; characters elegantly. After including <string> header file, you
strcpy(str1,"Hello"); can declare a string variable and use it with little fuss, because the
str2 = " World!"; burden of memory ownership is transferred to the string class
cout << str1 << str2; rather than the programmer (i.e. you!).
return 0;
} Listing 1.3

#include <iostream>
The text Hello World! will be displayed on screen. #include <string>
Do you know you can assign str1 to str2 and vice versa? using namespace std;

char str1[20]; main() {


char *str2; string s;
strcpy(str1,"Hello"); s = Peter;
str2 = str1; cout << "Hello " << s << "!" << endl;
return 0;
}
AND vice versa:
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;
>> known as extractor operator where user input is stored in for (x = 0; x < 10; x++)
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 : ;
cin >> A; In reverse:
cout << Enter another number : ;
cin >> B; for (x = 10; x > 0; x--)
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 {
being read, you can first declare a character array to be of the x++;
maximum size allowed: cout << x << ;
}
char name[20]; Try this:
cout << What is your name? \n;
cin >> name; while (++x < 10)
{
cout << x << ;
The program will take in the first 19 characters. The 20th character }
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 1.2.9.3 do ... while loop:
characters, but only the first 19 characters will be stored in name.
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;
1.2.10 Making choices case U: cout << U<< endl;
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
{
cout << You can vote << endl; continue to execute statements belonging to other cases. This is the
} fall through effect. Statements will be executed line by line until
else either a break statement or the end of the switch case
{ statement is encountered.
cout << Too young to vote << endl;
} switch(a_character)
{
case A:
case a: cout << A<< endl;
1.2.10.2 The switch case statement break;
case E:
If you have many choices, a more efficient code is to use the case e: cout << E<< endl;
break;
switch case statement. Note that the switch case statement
case I:
can be used to test integers and single characters only. case i': cout << I<< endl;
break;
Let me introduce to you: case O:
case o: cout << O<< endl; The goto command is useful when you want your program to skip
break; from its current position to execute instructions on a certain line.
case U: All you have to do is to label the line that you may want to skip to
case u: cout << U<< endl; with later a label name. For example:
break;
default: cout << Not a vowel\n;
} Listing 1.5

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

Pointers are often confusing to programmers of all levels, of this space. Subsequently, the integer value 9 that will be
especially beginners. Usually programmers run away (screaming) converted into binary format occupying 32 bits, i.e., 0000000
when I ask them about pointers. Such extreme response has since 00000000 0000000 00010001 is inserted into this memory
served as a constant reminder for me to very cautiously pace space.
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
control the way the memory of the computer is utilized as being 1st byte of x
instructed by your C++ program. In the olden days when RAM or
memory is significantly more expensive, this task of coding This 32-bit memory space has an address so that the computer may locate the
efficient programs is of utmost importance. However, careful variable x when required. For example:
memory management can be extremely tedious. Thankfully RAM 0x0012FF7C.
nowadays is much cheaper than before and we can be more relaxed The address points at the first byte of the memory space.
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 allocate
going to store the address of the integer, which is 0x0012FF7C,
32 unused bits (NB: An int data type is allocated 4 bytes which is
and not the integer value 9. A pointer variable is declared as
32 bits; a char 8 bits; and a double 64 bits). This memory space
follows:
that is allocated to x has an address which points at the first byte
int *ptr_x; cout << *ptr_x;
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 address
int *ptr_x; (2) 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 store
the address of an integer. cout << "Value" << "\t\t" << "Address\n";
cout << "x = " << x << "\t\t" << "&x = " <<
(3) ptr_x now stores the address of x, &x. In other words,
&x << endl << *ptr_x = " << *ptr_x <<
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 x, Yes, there is a subtle relationship between pointers and arrays. You
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 So what are the outcomes of the following codes? Go on, try it!
with 3 elements of type integer, leaving the remaining 7 elements
zero-initialized (yes, they are stored with 0s). cout << *arr+1;
IMPORTANT: and
Typing arr by itself actually means you are retrieving the address cout << (*arr)+1;
of the array, and not the contents of the entire array! Try it if you
do not believe me. Because both pointer variables and array variables store
addresses, you can assign one array variable to another pointer
cout << arr; variable (assignment is done using the operator =):

will produce an address which looks something like 0x0000FF24. int arr[10] = {88,99,101};
int *ptr_arr;
This address marks the beginning of the array arr, which
ptr_arr = arr;
coincidentally is also the address of the first element of the array
arr, i.e., the integer 88. Do not code ptr_arr = &arr; as in line (3) in Chapter 2.1.2
because arr here is already an address. By now, I hope you wont
Since arr is an array, the contents of its elements can be retrieved be too surprised if I told you that
using the usual index system, where first element is 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 contents of
Once initialized, the contents of an array can be modified
arr, which turns out to be the first element of the array. To select
and accessed via array index as well as pointers:
which element to retrieve, you simply add the index to the address,
arr, and prefix with the pointer, *, as shown below: cout << *(arr+1);
and
cout << 1st element of arr is : << *arr; cout << arr[1];
cout << \n2nd element of arr is : << *(arr+1);
...

(arr + 1) is the address of the second integer element in array 2.2.1 Using sizeof() to determine the number of
arr. In our example, (arr+1) would be the address 0x0000FF28 elements in an array
since arr is 0x0000FF24 (the address location is increased by 4
bytes because an integer occupies 4 bytes of memory space). C++ has a built-in operator called sizeof that gives the sizes of
data types in bytes. You can use it on both variables and data types:
int x = 9; or equivalently
cout << sizeof(x) << endl;
// variable x: 4 bytes cout << There are <<
cout << sizeof(int) << endl; sizeof(arr)/sizeof(arr[0]) << elements.;
// data type int: 4 2.2.2 Answer to Question in Chapter 1.2.7
Now it can be used to determine the number of elements in an
Iin Chapter 1.2.7, you were asked why can you only strcat str2
array. If you can correctly guess the output of the following codes,
with str1 but not the other way around?
then I am sure you will have no problems understanding how to
determine the array size: char str1[20];
char *str2;
double arr[10];
strcpy(str1, Hello);
double *ptr_arr = arr;
str2 = World!;
cout << sizeof(*arr) << endl; // 8
strcat(str1, str2); // OK
cout << sizeof(arr) << endl; // ? (1)
strcat(str2, str1); // NOT OK!!
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 also
enough to store 20 characters. str2 on the other hand is
holds the address of the array. Therefore sizeof(arr) will return
declared as a pointer variable and has just enough space to
80 (bytes) since a double occupies 8 bytes of space and there are
store the word World!. Appending World! behind
10 of them.
Hello is OK because of the extra space str1 has, but
appending to str2 is disallowed because str2 has just
In line (2), ptr_arr being a pointer variable, does not actually
enough space for itself.
know the size of the entire array it is pointer to. Its content is the
address of arr which occupies 4 bytes. Therefore, rather
surprisingly, line (2) yields 4 and not 80. 2.3 Functions
Thus, to determine the number of elements in an array, write:
2.3.1 What is a C++ function?
cout << There are <<
sizeof(arr)/sizeof(int) << elements.; In the context of C++, a function is a couple of lines of C++
instructions bundles together under one unique name. For example,
Since all arrays will at least have one element, you can also use the using a layman example, a series of instructions:
following codes:
Fry fish
cout << There are << Steam chicken
sizeof(arr)/sizeof(*arr) << elements.; Stir fry vegetables
Boil soup int ans = n;
Serve with rice for (int count = n 1; count>=1; count--)
{
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
perform a task or several similar tasks. Think of a unique name for your function as long as it is not a
reserved keyword in C++ (like while, include, bool, etc). It is
Say your math lecturer wanted his class to calculate 7! + 9!, where good practice to name the function as descriptively meaningful and
n! = n(n 1)(n 2) (3)(2)(1). While most students would take 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
C++. You probably noticed 7! + 9! involves: int factorial(int n) { ... }

1. A repeating task (factorial is performed twice) The task that a function performs usually depends on a certain
2. A recognizable pattern in that task (i.e., factorial): inputs. A factorial function would compute the factorial of n, n
being the input. To be correct, n is called the input formal
// Pattern to compute n! : parameter. Note the following when writing a function:
A function can return at most one value back to the program,
Place input parameter in parentheses, ( ), behind the sometimes none. You need to determine the data type of the value
function-name. that is being returned. Write this data type in front of the function.
In factorial, the obvious return value is the resulting factorial, n!,
int factorial( <input parameter> ) which is an integer.

Specify the data type of input parameter. If there is no return value, you must indicate void.

int factorial(int n) void factorial(int n) { ... }

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

Input can have a predefined default value which will be A function prototype is just a single line of code that states the
used in case no input value is provided by the program. inputs and output of a function. It performs no action other than to
warn the compiler of what to expect later in the function definition.
int factorial(int n = 2) Prototypes are optional but are recommended so that correct type
checking for arguments (i.e., values, constants, pointers, etc) and
New variables are declared according to the input parameter return values is enabled. In other words, it is used to check if the
list. However, they only exist within the local scope of the number of arguments and their data types in the calling function
function. They will be destroyed at the end of the function. correspond to the number of parameters and their types in the
called function when the function is called. A prototype contains
the header of a function without the detailed instructions found in
3. DETERMINE ITS RETURN TYPE the function body.
I recommend writing a function prototype before the start
int factorial(int n) { ... } of the main program. They are also usually written in a separate
header .h file.
}
Example of the factorial function prototype (remember semicolon):
// main() body Program entry point
int factorial (int n=2); // Calculate {[(5! + 1)! + 2]! + 3}! + 4
main()
{
2.3.5 Using functions to simplify complex tasks int result1, result2, result3, result4;
result1 = factorial(5,1);
An example to show why using functions can really simplify result2 = factorial(result1,2);
complex tasks: result3 = factorial(result2,3);
result4 = factorial(result3,4);
Lets say your math lecturer for no reason turned evil (forgive me, cout << The answer is << result4;
Mr Koh!) and assigned you a really tough question: return(0);
}
Find {[(5! + 1)! + 2]! + 3}! + 4.

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

Listing 2.1 factorial function main()


{
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)
{
for (int count=n-1; count>=1; count--) Nested functions are functions whose input values are determined
{ by the output of other functions. One condition is for the data types
n = n * count; of the input and corresponding output to be similar. Recall that the
} factorial function returns an integer which can in turn be passed
return (n + m); as an argument (input) to another factorial function.
#include <iostream>
using namespace std;
2.3.7 Recursive functions void makeWish(int age);
The function itself can be further simplified via recursion. A void makeWish(int age) {
recursive function actually calls itself, passing input values and cout << Happy << age << st birthday!;
expecting a return value at the same time. Recursive functions }
must have a termination condition to prevent it from calling itself
without end. main()
{
The factorial function can also be written via recursion: int buddy_age = 20;
// original age (as of yesterday)
int factorial (int n) makeWish(buddy_age);
{ cout << \nYou are now << (buddy_age+1);
// Termination condition // buddy_age is 20. Still not updated
if (n<=1) return 1; cout << years old!\n;
// 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) {


2.3.8 Pass by value and pass by reference cout << Happy << age << st birthday!;
age++;
}
Now that you can write functions and pass appropriate arguments
(values, constants, pointers, etc) in and out of them with ease, let
will not affect buddy_age in the main() program because age
me shift your focus slightly to another pressing issue surrounding
is simply a copy of buddy_age that exists only within the scope
functions the manner in which the arguments are passed (which
of the function. The solution is to pass the buddy_age argument
may alter the value of the original variable).
by reference. This means instead of making a copy of
buddy_age called age, you declare a reference variable age
Lets say you want to wish your buddy Happy 21st
which contains the address of buddy_age so that age is
birthday! and then say You are 21 years old now!.
effectively pointing to buddy_age. Consequently, any changes
Well, as of yesterday, he was still 20 years old and that is what
to age will effectively change its referenced value, i.e.,
your computer system would show of about his age.
buddy_age.
Listing 2.2 Pass by Value
To pass an argument by reference, just append the reference
operator, &, to the identifier of the input parameter:
Listing 2.3 Pass by Reference 2.3.9 Pass arguments by constant reference
#include <iostream>
OK, now there is a conflicting issue. What if the object to be
using namespace std;
passed is large but you are worried that by passing by reference,
void makeWish(int& age) { the object may be modified (intentionally or otherwise) within the
cout << Happy << age << st birthday!; function. Fortunately, you can prevent this from happening by
age++; using the const keyword which makes a referenced argument
} 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
Since age is a reference to buddy_age, buddy_age is number. Example:
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 ever in doubt what in the world you are passing, simply test the
pointer variable contains the address of the second element of the contents of your argument by printing it out on screen:
array.
cout << x;
Listing 2.4 Passing arrays
In this case, the screen displays an address which is the address of
#include <iostream> x. Since an address is passed, the input parameter of the function,
using namespace std; nextElement must be either an array of x as in Listing 2.4, or a
pointer variable which stores an address as shown below (if you do
int* nextElement(int arr[5]) not know the array size, use an empty array or a pointer
{
remember an empty array is equivalent to a pointer variable see
return ++arr;
} 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
Traditionally, a function performs a specific duty that is
is passed as an address, and not the contents of the entire array. If
programmed for it. C++ supports function overloading, adding
extra duties to existing functions. This is done simply by defining
multiple versions of a function with the same name, but the
versions must have different signatures. Lets say you want to Case 1:
write a function which returns the larger value of two inputs. A different return type does NOT change a function signature
However, you do not know what the data type of your inputs is
they can be either integers or doubles. Depending on the data type int maxi(int x, int y)
of the inputs, you have to return a similarly typed value. So you {
write the following for two integer inputs, x and y. return (x>y ? x : y);
}
int maxi(int x, int y) double 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
another function with the same name, but of a different signature. Case 2:
A signature of a function is constituted by the number, the order, When default values are provided for certain input parameters
and the data types of its input formal parameter. Note that different
return types do not change a function signature. The key to If the input parameters of a function were provided with default
overloading functions properly is to have distinguishable values, then the function has more than one signature.
signatures. Thus, an overloaded maxi function is:
int power(int x, int y=2)
double maxi(double x, double y) {
{ int total=1;
return (x>y ? x : y); while(y-- >= 1)
} 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 a
parameters whose types are both integer. The signature of the
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. Therefore, it
their data types are different double. Therefore it is legal.
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
programmers when attempting to overload a function: return (V*I);
} Sometimes I wish that I can declare just one variable but it bundles
would present the issue of conflicting signatures because it now together information of various data types. Structure is made for
has FOUR different signatures of three, two, one or zero integer this purpose.
inputs associated to it. Two of its signatures conflict with the 2.4.2 Anatomy of a structure
signatures of the first power function of two and one integer
inputs. Consider a new data type called car which has the following
attributes:

struct car
Case 3: {
When passing arrays and pointers char number_plate[10];
int year_of_manufacture;
Since arrays are always passed as pointers in function calls, input float engine_cc;
parameters like int *ptr and int arr[] are not distinguishable as bool transmission;
far as the signature is concerned. The following is the example // true for auto, false for manual
};
taken from Listing 2.4.

int* nextElement(int *ptr) To declare a structure, write the following code:


{
return ++ptr; struct car mycar1;
} 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;
2.4 Structures mycar1.transmission = FALSE; // manual
mycar1.year_of_manufacture = 2003;
// ==== Retrieval ====
2.4.1 Why use structures? cout << mycar1.number_plate;
cout << mycar1.year_of_manufacture;

2.4.4 Array of structures


So you have 5 cars. Use an array then:

struct car mycars[5];


strcpy(mycars[1].number_plate, BBB 8888);
mycars[3].year_of_manufacture = 1999;
Chapter 3 : Classes, The Basis Of Object-Oriented-Programming

3.1 Introduction to classes


1) The keyword struct is replaced with class (obviously!).
Object oriented programming (OOP) revolves around classes. E.g., class Complex.
What are classes? Well, from a class, you create objects (which is 2) A class is divided into 3 sections, called public:, private:
the objective of object-oriented-programming, right?). So what are and protected:. All members of a structure are inherently
objects? There is no simpler way of putting it than to tell you that public!
3) A class may have functions too (which include constructors
int x; and destructors)!!
There are several other differences but they are too advanced
creates an object x from the class int! So x is an (integer) object. for now. Details in later chapters.
Hope I didnt shock you. Well, you are absolutely correct if you
say that x is a variable of data type integer. Under OOP, x is In our Complex class, there should be a real and an imaginary
essentially an object created from the class int which is a class part, both of which are typed double. It should also consist of a
prewritten and provided by C++. And from one single class, you function called showNumber which when called will display on
can create as many objects as you like: screen the typical form of a complex number, i.e., x+yi. Yes, an
object not only has attributes like the real and imaginary values, it
int y; can have functions too! In addition, I will throw in another
int z; function called magnitude which calculates and returns the
int x_arr[10]; magnitude of this complex number, where its formula is given by
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(); cout << real << "+" << imag << "i";
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;
Listing 3.1 Complex class imag = 0;
cout <<
#include <iostream> "========CONSTRUCTION========\n";
#include <math.h> cout << "I'm being created! Yeah!\n";
using namespace std; cout << "My new value is ";
showNumber();
class Complex cout <<
{ "\n============================\n";
public: }
// Data members
double real, imag; // Destructor of class Complex
Complex::~Complex()
// Member function prototypes {
void showNumber(); cout <<
double magnitude(); "\n========DESTRUCTION========\n";
cout << "I'm being destroyed! Sob...\n";
// Constructor prototype cout << "My last value is ";
Complex(); showNumber();
cout <<
// Destructor prototype "\n============================\n";
~Complex(); }

private: main()
{
protected: Complex c_num;
}; // Note semicolon
cout << "\nAssigning new value : ";
// ==== Member functions of Complex ==== c_num.real=2;
void Complex::showNumber(void) c_num.imag=5;
c_num.showNumber(); members written under the private: can only be accessed by the
host object only, i.e., c_num only (not even objects created from
cout << "\nIts magnitude is : "; the same class, e.g., c_num2, c_num3), while members written
cout << c_num.magnitude() << endl;
under the public: section can be accessed by everybody
return(0);
}
(anywhere in the program). For now, just write every member
under public since it has the widest access.

Under public:, there are


3.2.1 Screen Output
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., ~Complex().
be created from this class must use this name. E.g.,
Lets go through the main() program:
Complex c_num;
Complex c_num;
Do not make the mistake of writing
declares an object from the class Complex. This line of code
Complex c_num(); // Wrong! instructs C++ compiler to allocate sufficient memory for this
object. In addition, the constructor (Complex::Complex()) is
The body of the class is enclosed within a pair of open called when this line is executed, initializing real and imag to
and close bracers, terminated with a semicolon. Within the body, zero. That explains why you will see the first 4 lines of the screen
there are three main sections, i.e., public:, private: and display output.
protected:. The data members and member functions of the class
are written here. You place the members under different sections
when you want them to have different access restrictions. In short,
static int cnum_count;
...
};

You can use the dot notation to access public data members /*
and member functions of c_num: Static member initialization is written straight after class
{}; Dont forget to write Complex::
c_num.real=2; */
c_num.imag=5; int Complex::cnum_count = 0;
and The following program demonstrates the usefulness of a static data
member. It is similar to Listing 3.1 except with the inclusion of the
c_num.showNumber(); // Displays 2+5i tatic cnum_count has been included:
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++
automatically destroys all objects created earlier. Before c_num is #include <iostream>
destroyed, the destructor function, Complex::~Complex(), is #include <math.h>
first called. This is why you get the final screen output. using namespace std;

class Complex
{
public:
double real, imag;
static int cnum_count;
Complex();
~Complex();
3.3 Static Members };

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

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 of public:
(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
moment. Later, two identical display lines 2 complex void showNumber();
double magnitude();
number(s) are the result of lines (2) and (3). Actually, they all
// Private data accessor functions
point to the same address regardless of whether access is via its void setValue(double real, double imag);
class or any one of the existing objects.
double getReal(); return real;
double getImag(); }
static int getCount(); // Get cnum_count
double Complex::getImag()
// Constructor prototypes {
Complex(); return imag;
Complex(double r); }
Complex(double r, double i);
void Complex::showNumber(void)
// Destructor prototype {
~Complex(); cout << real << "+" << imag << "i";
}
private:
double Complex::magnitude()
// Data members {
double real; return (sqrt(real*real + imag*imag));
double imag; }
static int cnum_count;
// Constructors of class Complex
}; // Note semicolon Complex::Complex()
{
// ==== Static members ==== real = 0; imag = 0;
int Complex::cnum_count = 0; cout << "Complex object (0+0i) created.\n;
cout << "There are " << ++cnum_count;
int Complex::getCount(void) cout << " Complex objects.\n\n";
{ }
return cnum_count;
} Complex::Complex(double r)
{
// ==== Member functions ==== real = r; imag = 0;
void Complex::setValue(double r, double i) cout << "Complex object (";
{ showNumber();
real = r; cout << ") created.\nThere are ";
imag = i; cout << ++cnum_count << " Complex
cout << "Value set :"; objects.\n\n";
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();
cout << ") created.\nThere are ";
cout << ++cnum_count << " Complex
objects.\n\n";
}

// Destructor of class Complex


Complex::~Complex()
{
cnum_count--;
cout << "Complex object being destroyed\n";
cout << cnum_count << " Complex object(s)
left.\n\n";
}
main()
{
cout << "\nProgram begins.\n";
cout << "There are " << Complex::getCount(); 3.3.1 public, private and protected
cout << " Complex objects.\n\n\n";
Complex c_num1;
c_num1.setValue(2,5); A brief summary of member access restrictions of each section:
Complex c_num2(7,8); public: Members can be accessed from anywhere of the
return(0); entire program, i.e., by everybody.
} private: Members can be accessed by member functions
in the same class. In other words, members can
be accessed by the host object only. Not even
Screen output: other objects created from the same class can
access private members.
protected: Members can be accessed by itself and its
subclasses (classes that inherit this class).

It is good practice to hide sensitive data members (place them


under the private section) so the (possibly malicious) public cannot
have direct access to them. For example, you do not want anybody
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
constructor and destructor, suitably so.
private: }
double real, imag;
You can also insert default values:
You then write public functions to access these private data
members. For example, Complex::Complex(double r, double i=0)
:real(r),imag(i)
public: {
void setValue(double real, double imag); }
double getReal();
double getImag(); Although initialization can be achieved with or without using the
init-list, they are not equivalent in general. For initialization
Protected members will come in handy when we learn about purposes, the init-list is the right choice.
inheritance in later chapters.
Note: Data members are initialized in the order they are listed in
the class declaration, not the order in the initialization lis:

3.4.2 Constructors class Complex


{
private:
You can overload a constructor as shown:
double real, imag;
// class declaration: real first, then imag
Complex::Complex(double r, double i)
public:
{
Complex(double r, double i):imag(i),real(r) {};
real = r;
/*
imag = i;
Initialization list begins with imag then
}
real. However real gets initialized first
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)
:real(r),imag(i) reference is a name for an existing object. Therefore, it should
{
be initialized or else it will be left pointing to a temporary that it is further prefixed by the tilda sign, ~. Its prototype and
value. member function typically look like this:

class Complex ~Complex(); // Prototype


{
private: Complex::~Complex() // Destructor function
Name& c_name; {
// ... cout << Thats all folks!\n;
}; }

Complex::Complex(Name& n):c_name(n) Even though you may survive without a destructor, good
{ programming practice encourages destructors be written. It is
// ... because when an object is created, the amount of available
}
memory decreases. Therefore when the object is destroyed, the
destructor should release the memory appropriately. Failure to do
You have a data member which is declared const but has no so will result in memory leak.
value yet.

class Complex
{
private: 3.4.4 A small task
const int max_count;
// ... By the way, for the sake of beautifying the output as good
}; programmers like to do, can you rewrite the showNumber()
function so that for negative imaginary numbers, instead of
Complex::Complex():max_count(99)
{ displaying, say, 5+-2i, it will display 5-2i?
// ...
} Sample Solution (give it a try first before peeking at answer):
void Complex::showNumber(void)
{
cout << real << (imag<0 ? "-" : "+")
3.4.3 Destructors << (imag<0 ? imag*-1 : imag) << "i";
}
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...
double x, y;
Complex c_num(4,5); x = c1->getReal() - c2->getReal();
Complex *p = &c_num; y = c1->getImag() - c2->getImag();
return sqrt(x*x + y*y);
}
3.5.1 Accessing Members
And this is how you access the members of c_num via its
pointer: 3.5.2 Object Arrays
p->showNumber(); Remember that an array is essential a pointer too (because an array
(*p).showNumber(); is represented by an address, just like pointers)! Let me show you
one way of finding the distance between two complex numbers
They are both equivalent. C++ provides the member accessor which are now stored in an array. Remember that this is just one of
operator -> for pointers so that you do not have to type * in order many ways to get the job done!
to retrieve the contents of c_num. Check out the following
example program that calculate the distance between two complex main()
numbers: {
Complex c_arr[2];
Listing 3.4 Complex class with pointers Complex c_num1(1,1), c_num2(4,5);
c_arr[0] = c_num1;
c_arr[1] = c_num2;
cout << "Distance is ";
// adopting the codes from Listing 3.3
cout << c_num1.calcDist(c_arr);
// An address is passed into calcDist
main()
return 0;
{
}
Complex c_num1, c_num2;
Complex *p1, *p2;
double Complex::calcDist(Complex *c_arr)
p1 = &c_num1;
{
p2 = &c_num2;
Complex c_dist( (*c_arr).getReal()
p1->setValue(1,1);
(*(c_arr+1)).getReal() ,
p2->setValue(4,5);
(*c_arr).getImag()
cout << "Distance is ";
(*(c_arr+1)).getImag() );
cout << c_num1.calcDist(p1, p2);
return 0;
Complex *c_ptr = &c_dist;
}
return c_ptr->magnitude();
double Complex::calcDist(Complex *c1, Complex *c2)
}
{

Você também pode gostar