COS1512 501 - 2010 - 3 - B
COS1512 501 - 2010 - 3 - B
COS1512 501 - 2010 - 3 - B
School of Computing
COS112V
Introduction to Programming 2
Hierdie studiebrief bevat die studiehandleiding vir COS112V. Aangesien die voorgeskrewe boek in Engels is,
verskaf ons die studiehandleiding ook in Engels. Indien u enige probleme met die Engels ondervind, kontak
asseblief een van die COS112V dosente.
Raadpleeg asseblief die Afrikaanse Woordelys vir die vertalings van belangrike terme wat in hierdie
studiehandleiding sowel as in die handboek gebruik word.
3 COS112V/501/3
Introduction
Welcome to COS112-V, an introduction to objects and the object-oriented programming environment. This
tutorial letter accompanies the prescribed book for COS112-V:
Walter Savitch. Problem Solving with C++, 7th edition. Pearson Education, Inc, 2009.
Note that this tutorial letter refers to the 7th edition of Savitch throughout. If you are using the 6th edition of
Savitch, please contact the module leader via e-mail to request a copy of the tutorial letter 501 for the 6th edition
of Savitch.
Tutorial Letter 101 specifies the sections in Savitch that are covered in COS112V. We repeat them here for your
convenience. Some of the work in the prescribed book is also covered by COS111U, and therefore only certain
sections and chapters are covered in COS112V. The path that COS112V follows through the prescribed book
can be outlined broadly as follows:
Section 1.2 (p12 -18) in chapter 1 provides a general overview over programming and problem-solving with a
brief introduction to object-oriented programming. Though most of chapters 4 and 5 have been covered in
COS111-U, section 4.6 (overloading functions) and section 5.5 (the assert macro) are included to ensure that
students have the necessary background knowledge to understand and implement object-oriented programming.
Chapter 6 uses file I/O streams as an introduction to objects and classes, and teaches students how to use pre-
defined classes. Chapter 8 builds on using pre-defined classes by introducing the standard class string.
Chapter 8 also covers C strings and provides a preview of the Standard Template Library (STL) with the
vector class. Chapter 9 further adds to the background knowledge necessary to understand and implement
classes and objects by discussing pointers and dynamic arrays. In Chapter 10 students learn how to define their
own classes in order to create an abstract data type (ADT). Chapter 10 also covers inheritance briefly in order to
make students aware of this concept. Chapter 11 continues to teach more techniques for defining functions and
operators for classes. Chapter 12 covers separate compilation, to allow placing the interface and implementation
of an ADT in files separate from each other and separate from the programs that use the ADT. In Chapter 14
recursion is introduced. In Chapter 15 single inheritance, i.e. deriving one class from another is covered. In
Chapter 17 function and class templates are covered, which will allow students to understand and use the STL.
The discussion of each chapter in Savitch given in this tutorial letter typically includes these subsections:
• Overview
• Learning Objectives.
The Overview indicates the main issues addressed in each chapter. The Learning Objectives of each chapter
are the skills you should acquire when mastering the content of the chapter. You will be tested on these skills in
4
the examination. These objectives can be achieved by doing the self-test exercises and the assignments,
implementing the examples given in the textbook and doing some additional exercises.
Supplementary information is given where we feel that the explanation given in the prescribed book is
inadequate. Please read these sections carefully, as you may not be able to complete the assignments without the
additional information.
5 COS112V/501/3
1.1 Overview
Much of what is covered in this chapter you probably have encountered somewhere in your programming studies
up to now. We want to bring two specific aspects in this chapter to your attention: the process of compiling a
C++ program, and the concept of object-oriented programming. Though you will not be examined on this
chapter as such, it serves as a brief orientation to the concepts covered in this course.
A text editor is used to type the program and make corrections if necessary. The program is then stored in a file
(the .cpp file) on a secondary storage device such as the hard disk. This program is called the source code or
source program. The DevC++ integrated development environment (IDE) provides the text editor we use in this
course, and forms the interface to the compiler, the linker and the loader.
In the next step, the programmer gives the command to compile the program. The compiler checks for syntax
errors and translates the C++ source program into machine language code (also referred to as object code and
stored in a file with extension .o). In a C++ system, the compiler invokes a preprocessor program automatically
before the compiler’s translation phase begins. The C++ preprocessor executes special commands called
preprocessor directives. Preprocessor directives typically call for the inclusion of other text files in the file that
has to be compiled and therefore perform various text replacements. We use the MinGW compiler. Errors
indicated by the compiler are corrected using the text editor.
C++ programs usually contain references to functions defined elsewhere, such as in private (user-defined)
libraries or the standard libraries. The code for the functions residing in the libraries have to be combined with
the object code produced by the C++ compiler. This is done by a linker. A linker links the object code produced
from your source code with the code for the missing functions to create the executable code (saved in a file with
the extension .exe).
6
Next, the executable code is loaded into the main memory for execution. This is done by the loader, which
transfers the executable program from disk to memory. Finally the computer executes the program.
Object-Oriented Programming
In object–oriented programming applications are organised around objects rather than processes. In the object-
oriented paradigm, a system is seen as a collection of interacting objects that models the interaction between
objects necessary to achieve the desired effect. In object-oriented programming, program components are
envisioned as objects that belong to classes and are similar to real-world objects; the programmer then
manipulate the objects and have them interrelate to achieve a desired result. With object-oriented analysis,
design and programming, the focus is on determining the objects you want to manipulate rather than the
processes or logic required to manipulate the data. Object-oriented programming involves three fundamental
concepts: encapsulation, inheritance and polymorphism.
Writing object-oriented programs involves creating classes, creating objects from those classes, and creating
applications, i.e. stand-alone executable programs, that use those objects. If they are designed properly, classes
can be reused over and over again to develop new programs.
An object generally represents a concrete entity in the real world such as a person, place or thing. Such an object
has certain characteristics or attributes and certain behaviours or actions. An object’s characteristics represent
data or facts that we know about the object. This is sometimes called the state of the object. The object’s
behaviours are actions that the object can take. These actions are expressed in functions or methods that execute
algorithms. Each object plays a specific role in the program design. The object interacts with other objects
through messages or function calls. Attributes and behaviours, i.e. an object’s data and algorithms, can be
encapsulated, or combined together to form a class.
A class is like a template or blueprint that can be used to construct or instantiate many similar objects. A class
can also be seen as a sort of user-defined data type, which we call an abstract data type (ADT). An object is an
7 COS112V/501/3
instance of a particular class. A class hides information by restricting access to it. In effect the data and methods
within an object are hidden from users to prevent inadvertent changes. Users of the class simply need to
understand how to use the interface or interaction between the object and its methods.
Through inheritance it is possible to define subclasses that share some or all of the parent class’s attributes and
methods, but with more specific features. This enables reuse of code.
Polymorphism describes the feature in languages that allows us to interpret the same word differently in different
situations based on the context in which it is used. In object-oriented programs this means that similar objects
can respond to the same function call or message in different ways.
8
4.1 Overview
In this chapter both predefined (library) functions and programmer-defined functions are discussed. This
includes the use of value parameters, function invocation, procedural abstraction, and local and global scope.
These concepts are covered in COS111U, but in section 4.6, overloading function names, which is not covered in
COS111U, is explained.
4.3 Notes
1. Study section 4.6.
2. Do the self-test exercises on page 268.
3. If necessary, work through the rest of the chapter to refresh your memory.
5.1 Overview
This chapter completes the description of C++ functions by discussing void functions and reference
parameters. All of these concepts are covered in COS111U. It also discusses testing and debugging functions.
In this regard we cover the assert macro in section 5.5.
5.3 Notes
1. Study section 5.5.
2. Do the self-test exercises on page 322.
3. If necessary, work through the rest of the chapter to refresh your memory.
4. Work through the notes on using the Debugger with the prescribed C++ software available in Appendix A
to Tutorial letter 101 as well as on the CD containing the prescribed C++ software.
10
6.1 Overview
This chapter explains how to accept input from a file and send output to a file by means of streams. In the
process of explaining streams, the basic ideas of what objects are and how to use them in a program, is
introduced.
The iostream header file is vitally important because it provides a consistent interface to all input/output in C++:
screen, keyboard and file. Furthermore, all input/output devices can be accessed in C++ through streams (using
operator overloading.) This input/output header file also provides access to a set of input/output routines that
can be used to output all the primitive (i.e. built-in) C++ types, and can be overloaded by the user to output any
user defined type. (Operator overloading is covered in Chapter 11.)
6.3 Notes
1. Study sections 6.1 to 6.3.
2. Do the self-test exercises on pages 349, 360, 367, 377 and 387.
In Display 6.1 the input-file stream variable in_stream is declared with the statement
ifstream in_stream;
The statement:
in_stream.open("infile.dat");
connects the file infile.dat (created as described above) to the input-file stream variable by means of the open
member function. If the input file and the program file have not been saved in the same directory, the full path
name for the input file should be supplied. In this example infile.dat is the external file name, and in_stream
the stream name used as the name of the file in the program.
Similarly, in Display 6.1 the output is written to the file outfile.dat. Output files are created by the program.
The output file outfile.dat will therefore be created by the program. We can read the content of the file using a
text editor, such as the DevC++ editor.
One can also use a variable of the standard string class to hold the name of a file given as input, as shown
below. Note that in this case when the name of the file is supplied as argument to the member function open,
the string variable must first be converted to a C-string with the c_str()member function of the string
class. The string member function c_str() converts a standard string to a character array.
string file_name;
ifstream in_stream;
cout << "Enter the name of the file to be processed: ";
cin >> file_name;
in_stream.open(file_name.c_str())
header files are arranged so that to #include<fstream> is sufficient to get the iostream header file
included as well. Note, however, that this may not be the case for all compilers.
Files from which data should be read as strings, ints, floats or a mixture of data types, e.g. ints and
chars, are called data files. Files which should be read and processed character by character, are called text
files. The way in which we read data files differ from the way in which we read text files.
The typical way to read a data file, i.e. a file containing values that should be processed as int, string or
float, is demonstrated below. Assume we have to process a data file called infile containing one int
followed by a string on each line:
ifstream infile;
infile1.open("InputFile.dat");
if (!infile)
{
cout << "Cannot open file "
<< "InputFile.dat" << " Aborting!" << endl;
exit(1);
}
int value;
string name;
while (infile>> value >> name) //this checks for the eof marker of infile
//before reading the data from the file
{
//process value and name
}
infile.close();
Processing the input file as a text file, i.e. a file consisting of characters which should be processed one by one, is
demonstrated below:
ifstream infile;
infile1.open("InputFile.dat");
if (!infile)
{
cout << "Cannot open file "
<< "InputFile.dat" << " Aborting!" << endl;
exit(1);
}
char ch;
infile.get(ch);
while (!infile.eof()) //explicit testing for eof of infile
{
//process ch
infile.get(ch);
}
infile.close();
13 COS112V/501/3
8.1 Overview
This chapter covers two topics that use arrays or are related to arrays: strings and vectors. Section 8.1 introduces
C string variables, which are inherited from C. C strings represent a string as an array with base type char.
Arrays have been covered in COS111-U. Chapter 7 also covers arrays, and you may refer back to it if you need
to refresh your memory. Section 8.2 discusses the standard string class, which was dealt with in COS111-U.
You will not be examined on section 8.2, but do refer to it if necessary. Section 8.3 introduces the container
class vector in the Standard Template Library. We do not cover the use of iterators to access vector elements
sequentially, though this is covered in Chapter 18 of Savitch.
8.3 Notes
1. Study sections 8.1 and 8.3.
2. Do the self-test exercises on pages 483, 489 and 518.
A vector is a fixed-length group of elements of uniform type, indexed by integer keys. As mentioned before,
vectors can be considered to be a generalization of the built in C++ array type. However, they have several other
notable features not associated with the basic array. The main advantages of using vectors is that the size of a
vector need not be fixed and can be changed during program execution, which is not possible with an array.
Vectors are also important when the ability to rapidly access arbitrary elements is necessary, because, as with an
array, individual elements in a vector can be directly indexed. The elements in a vector are assumed to be
unsequenced, but the operation sort in the STL can be used to place the elements of a vector in order.
The Standard Template Library (STL) standardises commonly used components through a set of containers
(lists, vectors, sets, maps, etc.) that hold objects, built-in objects and class objects. The containers keep the
objects secure and define a standard interface through which they can be manipulated. The STL also contains
standard algorithms that can be applied to the objects while they are in the container, for instance, to sort them,
compare them, merge objects into another container, find a specific object and many more operations. The STL
algorithms use iterators, which are similar to pointers, to operate on the objects in the containers. The algorithms
manipulate the objects in a standard way and the iterators ensure that the algorithms handle all and only the
elements in the containers, and no more. You do not need to be able to use the iterator access methods for
vectors, though more information on using iterators can be found in Chapter 18.
15 COS112V/501/3
9.1 Overview
This chapter introduces the concept of pointers and dynamic arrays.
9.3 Notes
1. Study sections 9.1 and 9.2 up to page 549.
2. Do the self-test exercises on pages 538 and 548.
3. We do not cover the subsections on Pointer Arithmetic and Multidimensional Dynamic Arrays.
16
10.1 Overview
This chapter teaches you how to define your own classes. A class is a data type whose variables are objects.
You have already used some pre-defined data types such as int, char, string and ifstream. The
chapter first introduces structures as a first step toward defining classes. A structure (struct) can be viewed as
kind of a simplified class without member functions.
10.3 Notes
1. The terms structure and struct are often used as synonyms. The COS111-U study guide uses the term
struct, while Savitch uses structure to refer to the same concept.
2. Savitch uses the term class definition for the declaration of a class. This is also known as an interface for
the class or the class specification. Remember that a class is a data type, and the class definition specifies
what attributes the data type have, i.e. which values it can hold, as well as its behaviour, i.e. what it can
do. When we declare variables of a specific class, we say we instantiate objects of the class.
3. Study sections 10.1 to 10.4.
4. Do the self-test exercises on pages 569, 577, 586, 593, 604, 614 and 623.
A const member function promises not to change any class data that it uses. A use of const in a class object
declaration promises that the function won’t call any other function with the potential to change that object’s
data members. The compiler tries to enforce these promises.
Use of the const keyword requires consistency. As shown in the BankAccount example below, if you
declare a member function const in a class definition, you must use the const keyword in your definition of
that member function. If you use a const object for an argument, and do not declare the function with a
const parameter, you should get an error or a warning from your compiler.
17 COS112V/501/3
To implement this in the class definition for the BankAccount class on page 588:
(a) Change the function prototypes for the accessor functions get_balance( ) in line 17 and
get_rate( ) in line 19 to:
double get_balance( ) const;
double get_rate( ) const;
(b) Change the headings of the member function definitions for get_balance( ) in line 85 and
get_rate( ) in line 90 to:
double BankAccount::get_balance( ) const
double BankAccount::get_rate( ) const
In this way the compiler will give an error message if any statement in the body of either
get_balance( ) or get_rate( )attempts to change a data member. Pages 658 to 661 in chapter 11
further explains this concept.
18
11.1 Overview
This chapter continues teaching you how to define your own classes. It introduces friend functions, and teaches
you how to overload common operators such as +, -, * and / so that they can be used in the same manner as
with predefined types such as int and double. It also explains how to use classes of arrays, and arrays as data
members of a class. Section 11.4 covers classes and dynamic arrays. This includes how to write destructors,
copy constructors and how to overload the assignment operator.
11.3 Notes
1. Study sections 11.1 to 11.4, Appendix 7 on page 1044 and Appendix 8 on page 1047.
2. Do the self-test exercises on pages 643, 656, 662, 667, 669, 679, 685, 689, 701 and 705.
3. Note the use of the const modifier as explained on page 658. We use this trick to prevent accessor
functions from modifying data members.
4. Class definitions typically include a destructor.
The general form of functions to overload the binary operators as member functions of a class follows:
Function prototype (to be included in the definition of the class):
returnType operator#(const className&) const;
19 COS112V/501/3
where # stands for the binary operator, arithmetic (e.g. +, -, *, /) or relational (e.g. ==, !=, >, >=, <, <=, &&, ||)
to be overloaded; returnType is the type of value returned by the function; and className is the name of
the class for which the operator is being overloaded.
Function definition:
returnType className::operator#(const className& otherObject) const
{
//Algorithm to perform the overloading operation
return value;
}
Compare this with the general syntax to overload binary operators as non-member (friend) functions:
Function definition:
returnType operator#(const className& firstObject,
const className& secondObject) const
{
//Algorithm to perform the overloading operation
return value;
}
The process of overloading a unary operator is similar to that of overloading binary operators. The only
difference is that in the case of binary operators, the operator has two operands. In the case of unary operators,
the operator has only one operand. So, to overload a unary operator for a class, note the following:
1. If the operator function is a member of the class, it has no parameters.
2. If the operator function is a non-member of the class (i.e. a friend function), it has one parameter.
We use the class Money from chapter 11, and overload the pre-increment operator ++. We first show how to
overload the pre-increment operator ++ as a friend function and then as a member function.
20
The general syntax to overload pre-increment operator ++ as a non-member function in prefix position can be
described as follows:
Function definition:
className operator++(className& incObject)
{
//increment object by 1
return incObject;
}
Following this general syntax, we can overload the increment operator++ in prefix position as a non-member
function as follows:
Function prototype (to be included in the definition of the class):
friend Money operator++(Money & M);
Function definition:
Money operator++(Money & M)
{
++M.all_cents;
return M;
}
The general syntax to overload the pre-increment operator ++ as a member function in prefix position can be
described as follows:
Function definition:
className className:: operator++()
{
//increment object by 1
return *this;
}
Following this general syntax, we can overload the increment operator++ in prefix position as a member
function as follows:
21 COS112V/501/3
Function definition:
Money Money::operator++()
{ ++all_cents;
return*this;
}
Initially, we will show the destructor, assignment operator and copy constructor for a simple class, IntCell
that simulates a memory cell containing an integer value. The class declaration and implementation for class
IntCell is shown below. (Note that we use separate compilation in this example. If you have not studied
separate compilation yet, just ignore all the pre-processor directives starting with #.)
Specification (IntCell.h):
#ifndef INTCELL_H
#define INTCELL_H
class IntCell
{
public:
IntCell (int Value = 0); //constructor
int get_Value( ) const; //accessor
void set_Value(int x); //mutator
private:
int Value;
};
#endif
22
Implementation (IntCell.cpp):
#include "IntCell.h"
void IntCell::set_Value(int x)
{
Value = x;
}
11.6.1 Destructor
A destructor is a member function that is called automatically when an object of the class goes out of scope at
the end of a function, or is deleted explicitly by a delete statement. Destructors can do any “housekeeping”
necessary, such as to delete all dynamic variables created by the object. Just as the compiler creates a default
constructor automatically if no constructor has been specified for the class, the compiler creates a destructor
automatically if one is not included in the class definition. A class has only one destructor with no arguments.
The name of the destructor is distinguished from the no-parameter constructor by the tilde symbol ~.
To include a destructor for the class IntCell, we add ~IntCell as a member function to the class definition
(specification). The implementation for the destructor function ~IntCell, is as follows:
~IntCell ( )
{ //nothing to be done since IntCell contains only an int data member.
//If IntCell contained any class objects as member variables their
//destructors would be called
};
IntCell B(C);
but not
B = C; //Assignment operator
• When, as a parameter, an object is passed using call by value (instead of by & or const &)
• When the return value of a function is an object
In the first case the constructed objects are explicitly requested. In the second and third cases temporary objects
are constructed. Even so, a construction is a construction and in both cases we are copying an object into a
newly created object.
By default the copy constructor is implemented by applying copy constructors to each data member in turn. For
data members that are primitive types (e.g. int, double or pointers), simple assignment is done. This is also
the case for the data member Value in class IntCell. For data members that are themselves class objects,
the copy constructor for each data member’s class is applied to that data member.
As mentioned on page 705 in Savitch, to make sure that the object is not copied to itself, an alias test is done
with
if (this != &rhs) //standard alias test to avoid self-assignment
An additional keyword in C++, the pointer this points at the current object. We can think of the pointer this
as a homing device that, at any instant in time, tells us where we are. (Also see Appendix 7 on page 1044 in
Savitch for more on this). The pointer this thus refers to the address of the current object, while the unary
address operator & in combination with rhs, as in &rhs, refers to the address in memory where rhs is stored.
If these two addresses are the same, i.e (this == &rhs), the same object appears on both sides of the
assignment operator and the operation should not be executed.
If the object is not copying to itself, the operator= is applied to each data member. IntCell has only one
data member Value, so only one statement is needed:
Value = rhs.Value;
*this is the current object, and returning *this will return a reference to the current object. This allows
assignments to be chained, as in a = b = c.
Suppose that the class contains a single data member that is a pointer. This pointer points at a dynamically
allocated object. If we adapt the specification for class IntCell so that its single member is a pointer to an
integer, the specification and implementation changes to what is shown below. Note that the default destructor,
copy constructor and assignment operator will be used since we do not define our own:
Specification (IntCell.h):
#ifndef INTCELL_H
#define INTCELL_H
class IntCell
{
public:
IntCell (int Value = 0); //constructor
int get_Value( ) const; //accessor
void set_Value(int x); //mutator
private:
int *Value;
25 COS112V/501/3
};
#endif
Implementation (IntCell.cpp):
#include "IntCell.h"
IntCell::IntCell(int InitialValue)
{
Value = new int(InitialValue);
}
int IntCell::get_Value( )const
{
return *Value;
}
void IntCell::set_Value(int x)
{
*Value = x;
}
The output from the application below in which class IntCell is used, exposes problems with accepting the
default destructor, copy constructor and assignment operator:
#include <iostream>
#include "IntCell.h"
using namespace std;
int main() {
IntCell a(2);
IntCell b = a;
IntCell c;
c = b;
a.set_Value(4);
cout << a.get_Value() << endl << b.get_Value() << endl << c.get_Value()
<< endl;
return 0;
}
26
Output:
4
4
4
Press any key to continue . . .
The output produces three 4s, even though logically only a should be 4. The problem is that the default copy
constructor and the default operator= both copy not the objects being pointed at, but simply the value of the
pointer Value. Thus a.Value, b.Value and c.Value all point at the same int value. This is called
shallow copying: i.e. the pointer rather than the contents of the address to which the pointer is pointing, is
copied. Typically, we would expect a deep copy, in which a clone of the entire object is made.
A second less obvious problem is a memory leak: the int initially allocated by a’s constructor remains
allocated and needs to be reclaimed. The int allocated by c’s constructor is no longer referenced by any
pointer variable. It also needs to be reclaimed, but we no longer have a pointer to it. This is because the default
destructor does nothing. We have to call delete ourselves. To fix these problems, we implement the Big
Three, as shown below:
Specification (IntCell.h):
#ifndef INTCELL_H
#define INTCELL_H
class IntCell
{
public:
IntCell (int Value = 0); //constructor
private:
int *Value;
};
#endif
27 COS112V/501/3
Implementation (IntCell.cpp):
#include "IntCell.h"
IntCell::~IntCell( ) //destructor
{
delete Value;
}
Running the same application as before, now gives the correct output, as shown below:
Output:
4
2
28
2
Press any key to continue . . .
To summarize, when a class has pointer members and a constructor allocates memory from the free store you
need a copy constructor to guarantee correct initialization of an object of that class type from another object of
that class type. And you also need a destructor to guarantee that free store memory pointed to by members are
deallocated when the class object goes out of scope. If you assign objects of such a class to one another, then
you need to overload the operator = to guarantee that the data pointed to by objects’ pointers is copied, and not
just the pointers.
Note that defining operator = has no effect on the behavior of the class objects during copy
construction. Similarly, defining a copy constructor does nothing for the assignment operator. Operator =
is invoked when assignment is made to a class object. The destructor is called when an automatic object goes
out of scope, and when the destructor is called explicitly.
29 COS112V/501/3
12.1 Overview
This chapter teaches you how to divide an ADT into separate specification and implementation files. It also
discusses how to implement separate compilation as well as conditional compilation using pre-processor
directives. This chapter also discusses namespaces. Creating your own namespaces is beyond the scope of this
module, but you do have to understand the concept of a namespace.
12.3 Notes
1. Study sections 12.1 to 12.2 up to page 743.
2. Study the sections on Include Directives and Namespaces on pages 82 and 83, and Namespaces Revisited
on page 256 to 258. In this course we will only use the standard namespace std.
3. Do the self-test exercises on pages 740.
Programs that use user-defined classes usually also use multiple source files. Typically the definition
(specification) of the class will be placed in a .h file, called the interface or specification file, while the
implementation will be placed in a .cpp file. This offers, among others, benefits such as compiling the files
separately and software reuse. For example, the interface may be required (with #include) on multiple
occasions within one or more projects’ code, but the implementation need only be compiled once.
To use separate compilation, you create a project to which the files to be included can be added. See appendix A
in Tutorial Letter 101 for instructions on how to do this in DevC++.
All preprocessor directives begin with #, and only whitespace characters may appear before a preprocessor
directive on a line. Preprocessor directives are not C++ statements, so they do not end in a semicolon (;).
To prevent multiple declarations of a class, we use the pre-processor directives #ifndef, #define and
#endif to control the inclusion of the header file so that the same definitions are not included repeatedly. This
is called ‘conditional compilation’.
The #include preprocessor directive causes a copy of a specified file to be included in place of the directive.
The two forms of the file inclusion directive (#include <filename> and #include "filename")
indicate where (which directory) the preprocessor should find the file to be included.
Once the preprocessing has been done, the compiler translates the source code into object code, stored in a .o
file. The linker then links the object code with the code from libraries for functions that are included (with
#include) to create the executable code, stored in a .exe file.
31 COS112V/501/3
14.1 Overview
This chapter introduces recursion. Recursion is a difficult topic to grasp. However, it’s very easy to apply once
you understand it. Recursion means allowing a function or procedure to call itself. It keeps calling itself until
some limit is reached.
14.3 Notes
1. Study sections 14.1 and 14.2 up to page 831.
2. Do the self-test exercises on page 822, 826 and 830.
14.4 Recursion
The summation function, designated by an uppercase ∑ (Sigma) in mathematics, is a popular example of
recursion:
n n-1
∑i = ∑i + n
i=1 i=1
sum(n) = 1 if n = 1
= sum(n-1) + n if n > 1
Recursion works backward until a given point is reached at which an answer is defined (the base case), and then
works forward with that definition, solving the other definitions which rely upon that one.
All recursive procedures/functions should have some sort of test to stop the recursion. Under one condition,
called the base condition, the recursion should stop. Under all other conditions, the recursion should go deeper.
In the example above, the base condition is if (num == 1). If you don’t build in a base condition, the
recursion will either not take place at all, or become infinite.
33 COS112V/501/3
15.1 Overview
This chapter introduces the concept of inheritance, a key feature of object-oriented programming. Inheritance is
the ability to define new classes from existing classes. The new classes that we create from the existing classes
are called derived classes; the existing classes are called the base classes. The derived classes inherit the
properties of the base classes. So rather than create completely new classes from scratch, we take advantage of
inheritance and reduce software complexity. We only cover section 15.1: Inheritance Basics. Polymorphism is
not covered in this module.
15.3 Notes
1. Study section 15.1 up to page 879.
2. You can safely ignore all references to namespaces other than std. In Display 15.1 on page 858, for
example, lines 9 and 28 can be omitted, and the same applies to the other displays in this chapter.
3. Do the self-test exercises on pages 872 and 879.
15.4 Inheritance
15.4.1 The Purpose of Inheritance
The primary purpose of inheritance is to reuse code by exploiting the ‘is-a’ relationship between objects.
Software reusability saves time in program development. It encourages the reuse of proven and debugged high-
quality software, thus reducing problems after a system becomes functional. Significant overlap between two
classes indicates a potential case for inheritance.
34
In an example of ‘is-a’ relationships, we might think of a class of (general) vehicles, and then think of a kind of
vehicle for carrying passenger (automobiles), another kind that is small and carries cargo in the open (pickup
truck), still another that carries cargo in an enclosure (van), others yet that carry cargo enclosed and are
articulated - tractor-trailer). The Truck, Van, and Car classes inherit their common features from the class
Vehicle.
In these cases, a car is a vehicle, so are pickups, vans, and tractor-trailers. A car ‘is a’ vehicle; a pickup truck ‘is
a’ vehicle, and so on. The class abstraction of vehicle is the base class and the vehicles that bear the ‘is a’
relation to the base class are derived classes. In this example, the base class contains the features common to all
types of vehicles. The derived class inherits all those common features and adds its own, extra, additional
features to the base class to form its distinctiveness. In this way, the common features (commonalities) are
encapsulated in the base class, and the distinctions are encapsulated in the derived class. The commonality is
passed from the base class to the derived class with inheritance. A derived class is therefore more specific than
its base class. This relationship is known as the ‘is-a’ relationship. The ‘is-a’ relationship specifies that one
abstraction is a specialization of another. If we write this example in
C++, we have
class Vehicle {/* . . . */};
class Car : public Vehicle { /* . . . */};
class Truck : public Vehicle { /* . . . */};
class TractorTrailer: public Truck {/* . . . */};
class StationWagon : public Car {/* . . . */ };
We see that inheritance creates a new class, called the derived class, that is an extension, specialization or
modification of one or more existing classes, called the base classes. In the situation where there is only one
base class, we have single inheritance. Where there is more than one base class, multiple inheritance occurs. In
this, example, we could illustrate multiple inheritance by defining
class Emergency {/* . . . */};
and have had a class PoliceCar inherit from both class Car and class Emergency. Or we could have a class
Ambulance inherit from both class Truck and class Emergency. The PoliceCar and Ambulance would
inherit features from Car and the derived class adds its distinguishing features. Since multiple inheritance is
beyond the scope of this module, we will not discuss it further.
Notice that the ‘is-a’ relationship is transitive (that is, a TractorTrailer is a Truck, a Truck is a
Vehicle, and therefore a TractorTrailer is a Vehicle), but it is not reflexive (that is, not all
Vehicles are TractorTrailers). Another way of expressing the ‘is-a’ relationship is to say that a
35 COS112V/501/3
TractorTrailer is a kind of Truck and that an Truck is a kind of Vehicle. See also text box on page
867 in Savitch.
In addition to the ‘is-a’ relationship that represents inheritance, two other relationships between abstractions are
commonly used on object-oriented design, namely the ‘has-a’ and ‘uses-a’ relationships. The ‘has-a’
relationship says that some object is part of another. The ‘has-a’ relationship implies containment. For example,
a car has an engine. The ‘uses-a’ relationship says that one object uses another object in some way. This usually
realized by one object communicating with another via member functions. For example, suppose the operating
system has a clock object that maintains the current date and time. The clock object has member functions that
return the current date and time. Other objects that need the date or time, use the clock object by calling the
appropriate member functions to fetch the current date or time.
So what does public inheritance mean? Consider the classes Base and Derived:
class Base
{
public:
int x;
protected:
int y;
private:
int z;
};
Public members of the Base class are public members of the Derived class too. The Derived class inherits
all data members and all member functions from the Base class, but it can access only those that are not
private. That is, a derived class can access public and protected members of its base class. In the example, class
Derived can access x and y directly but not z, because z is private in class Base. If we want to access z
which is a private data member in the Base class, then we can access z in one of two ways:
36
(2) or we could make z protected (more about this on page 870 in Savitch).
Now consider the following client program that instantiates objects of type Base and Derived:
int main( )
{
Base b;
Derived d;
cout << b.x;
cout << d.x;
//cout << b.y;
//cout << d.y;
//cout << b.z;
//cout << d.z;
}
Since x is public in class Base, it is also public in class Derived through public inheritance. Therefore we
can access x in the client program. But we cannot access y because it is a protected member of both Base and
Derived. And of course we cannot access z because it is private.
Note, that the access specifier for class inheritance defaults to private. If class Derived has base class
Base:
class Base {/* . . .*/};
class Derived : access-specifier Base
{ /* . . . */};
//where access-specifier is one of public, protected, private
then the access that is granted to member functions of class Derived depends on the access-specifier selected.
If the access-specifier is omitted, access to all members of the base class automatically defaults to private. This
would mean that Derived has no access to the member functions and data members of Base.
37 COS112V/501/3
17.1 Overview
This chapter discusses C++ templates. C++ templates provide a way to reuse code by defining functions and
classes that have parameters for type names. Templates are very useful when implementing generic constructs
like vectors, stacks, lists and queues, which can be used with any arbitrary type.
17.3 Notes
1. Study sections 17.1 and 17.2.
2. Once again, you can safely ignore all references to namespaces other than std, such as in lines 12 and 49
of Display 17.4.
3. The MinGW compiler does not allow separate compilation of templates. Therefore you need to include
the template definition (i.e. its declaration and the implementation thereof) in the same (header) file. Note
that you can place the template definition in a separate file and use a #include directive to include the
template definition when you need to use it.
4. Note that the use of the keyword class in a template prefix does not mean that you can write only a class
name there. You can use any type name already defined in the language such as, int, float, double,
or any identifier that is a user defined type (such as a class, structure or enum).
5. Do the self-test exercises on pages 949, 956 and 965.
38
Afrikaanse Woordelys
Vir belangrike terme wat gebruik word in die studiegids en handboek, gee ons die Afrikaanse vertaling.
ENGLISH AFRIKAANS
©
Unisa 2010