Académique Documents
Professionnel Documents
Culture Documents
1
Chapter 1: Basics For Beginners
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:
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
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
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.
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
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;
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.
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.
14
int factorial( <input parameter> ) If there is no return value, you must indicate void.
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). }
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: }
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);
}
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;
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.
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
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.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
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;
public:
void setValue(double real, double imag);
double getReal();
double getImag();
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.
29
{
cout << real << (imag<0 ? "-" : "+")
<< (imag<0 ? imag*-1 : imag) << "i";
3.4.3 Destructors }
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