CSCI 207: Fundamentals of Data Structures & Algorithms: Review of Object Oriented Programming Basics

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 17

CSCI 207: Fundamentals of Data Structures &

Algorithms

Lecture 2
Review of Object Oriented Programming Basics

1
Inheritance
▪ "A relationship amongst classes whereby one class shares the structure and/or
behaviour defined in one (single inheritance) or more (multiple inheritance) other
classes. Inheritance defines an "is-a" hierarchy among classes in which a member
class inherits from one or more super classes".

▪ The single most important rule in C++ object oriented programming is that public
inheritance means "is-a". Ignoring this rule is probably the most common cause of
poor design when developing a C++ application.

▪ For example we can clearly state that a "student is a person".

class TPerson {...... }; // Declaration of class Person


class TStudent : public TPerson { ...... }; // Declaration of class Student,
// extending class Person

▪ This is an example of single inheritance, where class TStudent inherits the state
and behaviour from the TPerson class. The alternative relationship to "is-a" is "has-
a" e.g. a Person has-a name. The has-a relationship is achieved through the use of
layering, where an object is contained within another object.
2
Inheritance
▪ A Base class is a class from which another class inherits, e.g. TPerson is a base
class. A Derived class is a class that inherits from one or more classes, e.g. TStudent
is a derived class. Alternative Terms:
Super class = Base class
Sub class = Derived class
inherits = extends

▪ Let’s have a look at what we mean by inheriting state and behaviour of a class:
class TPerson
{
private:
// data members
char _Name[20];
char _Address[80];

public:
// Constructor
TPerson(const char* Name, const char *Address);

// selectors to provide access to the data members


const char *Name() const { return _Name; }
const char *Address() const { return _Address; } }; 3
Inheritance
▪ The class TPerson contains data members representing the state of a person.
The data members and member functions present in class TPerson DO NOT need
to be duplicated in class TStudent.
// Class TStudent inherits the state and behaviour from class TPerson.
class TStudent : public TPerson
{
private:
// Data member specific to class TStudent
int _DegreeCode;

public:
// Constructor
TStudent(const char* Name, const char *Address, int DegreeCode);
// Selector function for class Student only
int DegreeCode() const { return _DegreeCode; }
};

4
Inheritance
TPerson Person("Fred", "Germany"); // Create a Person object
cout << Person.Name() << " lives in " << Person.Address() << endl;

A TPerson object is created and initialised by the class constructor. The selector
functions are then used to access the data members. The result as one expects is
"Fred lives in Germany"
TStudent Student("Bill", "UK", 5150); // Create a Student object
cout << Student.Name() << " lives in " << Student.Address() << ". Degree code = "
<< Student.DegreeCode() << endl;

A TStudent object is created and initialised by the class constructor. The selector
functions for both the TStudent class and the base class (TPerson class) are used to
access the data members within the class hierarchy. The result is "BilI lives in UK.
Degree code = 5150“

From this example the TStudent class is clearly an extension of the TPerson class.

Member functions from either class can be called for the TStudent object. The
converse is not true, it is a compiler error to call the member function DegreeCode()
on a TPerson object. 5
Inheritance
TPerson::TPerson(const char* Name, const char *Address)
{ if (strlen(Name) < sizeof(_Name))
strcpy(_Name, Name);
else
_Name[0] = 0;

if (strlen(Address) < sizeof(_Address))


strcpy(_Address, Address);
else
_Address[0] = 0; }

The constructor for the TPerson class copies the name and address parameters to
the respective data members. To avoid duplication of this initialisation code, it would
be useful if the TPerson class constructor could be called from within the TStudent
class's constructor. This is achieved by using the constructors initialisation list.
TStudent::TStudent(const char* Name, const char *Address, int DegreeCode)
:TPerson(Name,Address),_DegreeCode(DegreeCode)
{} // CORRECT

TStudent::TStudent(const char* Name, const char *Address, int DegreeCode){


TPerson(Name, Address); // Incorrectly creates a temporary TPerson object
_DegreeCode = DegreeCode; // Correctly initialises the _DegreeCode data member
} // INCORRECT
6
Inheritance – Access Control
It is time to expand our concept of access control to include inheritance. Consider a new
member function CalcTuition within TStudent class that calculates the tuition fees for
the student based on the student's address.
double TStudent::CalcTuition() const
{
if (strcmp(_Address, "UK")==0); // Compiler Error
return 1000.0;
else
return 2000.0;
}

The use of _Address produces a compiler error. The data member _Address is declared
private within class TPerson. A private member is not visible to derived class.

We have two options:


1. Change the privilege level of _Address to either protected or public.
2. Use the public selector member functions already available in class TPerson.

7
Binding
Let’s try and extend the TPerson and TStudent classes to include output streaming.

ostream &TPerson::Write(ostream &out) const


{
out << "Name: " << _Name << endl;
out << "Address: " << _Address << endl;
return out;
}

The member function Write() provides the output for the TPerson class’s data members.

ostream &TStudent::Write(ostream &out) const // Overload the Write() function


{
TPerson::Write(out); // Call the write function within the person class
out << "DegreeCode: " <<_DegreeCode << endl;
return out;
}

We have overridden the Write() function within the TPerson class by placing a Write()
function within TStudent class. To reduce code duplication, TStudent::Write() calls
TPerson::Write() to output the TPerson class' data members.

8
Binding
TStudent Student("Bill", "UK", 5150);
Student.Write(cout);

Produces the following output.

Name: Bill
Address: UK
DegreeCode: 5150

This example illustrates that a member function within a base class can be overridden
by the declaration of a member function within the derived class, provided that both
functions have the same identity (i.e. name, number and type of parameters and
whether constant or not).

9
Binding
"Binding denotes the association of a name (e.g. a function call) with a class.” We will
now consider the case of pointers.

TPerson *Ptr;
Ptr = new TStudent("Anne", "UK", 5151);
Ptr->Write(cout); // Output Name: Anne Address: UK
delete Ptr;

Ptr to a TPerson object can point to TStudent object. The C++ language permits this
syntax only because TPerson is above TStudent in the same object hierarchy, i.e.
TStudent is derived either directly or indirectly from TPerson. Assuming both classes
have function Write(), which of the two Write member functions are called?

In this case it is TPerson::Write() The reasoning is simple when we consider the


operation of the compiler.

For non-virtual functions (i.e. standard functions) the compiler determines which
function to call at compile time. This process is known as static binding. At compile
time, the only information available to the compiler is that Ptr is of type
TPerson*. The compiler therefore determines that the Write() function call is
associated with TPerson class and statically binds the call to TPerson::Write()
10
Virtual Functions
"An operation upon an object. A virtual function may be defined by derived classes;
thus for a given object, it is implemented through a set of methods declared in various
classes that are related via the inheritance hierarchy." Virtual functions inform the
compiler to perform dynamic binding on the function call.

In C++, a virtual function is denoted by the virtual keyword preceding the function
declaration in the class declaration.

class TPerson
{
virtual ostream &Write(ostream &out) const;
};

At runtime, the type of the object pointed to by Ptr is determined and cross
referenced in the virtual table to give, in this case, the function TStudent::Write()

11
Virtual Functions
When Write() is made a virtual function, the output from the above example
changes to:

Name: Anne
Address: UK
DegreeCode: 5151

The use of a non‐virtual function in the previous example is obviously incorrect and
can give some surprising results.

A simple rule to avoid possible problems is to ensure that if a function is being


overloaded in a derived class then it is declared virtual in the
base class.

12
Virtual Functions
//Simple list containing five people is created. A loop writes each object in turn.
TPerson *List[5]; // List is an array of pointers to TPerson objects
List[0] = new TStudent("Bill", "UK",5150);
List[1] = new TPerson("Fred", "Germany");
List[2] ‐ new TStudent("Anne", "UK",5151);
List[3] = new TPerson("Mary", "France");
List[4] = new TPerson("Tom", "UK");

for (int i=0; i<5; i++)


List[i]‐>Write(cout); // Call Write() function for each object on the list

delete List[4];
...
delete List[0];

This simple example illustrates the power of the virtual functions and the inheritance
mechanism. A heterogeneous list of objects is created and the compiler automatically
determines the appropriate Write() function to call.

Virtual functions appear very powerful, but there is an overhead. The virtual mechanism
relies on dynamic binding which by its nature impacts on the application performance.

13
Polymorphism
Polymorphism = the ability to take on many forms.

In object-oriented programming, polymorphism refers to a programming language's


ability to process objects differently depending on their data type or class.

Polymorphism is considered to be a requirement of any true object-oriented


programming language

Usually polymorphism refers to the ability to redefine methods for derived classes.

For example, given a base class TShape, polymorphism enables the programmer to
define different Area() member functions for any number of derived classes, such as
circles, rectangles and triangles.

You can define an object as of type TShape and later specify that this particular object
is a rectangle. No matter what shape an object is, applying the Area() method to it
will return the correct results.

14
Inheritance ‐ Abstract Classes
The form of inheritance used in the TPerson / TStudent example is an illustration of
"inheritance of the implementation". The Student class inherited the implementation
of the data members and member functions ( e.g. _Name and Name() ) from the
TPerson class.

There is another form of inheritance, "inheritance of the interface”. As the name


suggests this type of inheritance only inherits the interface of the parent class and not
the implementation.

The implementation for the inherited functions is provided by definitions in the


derived class. In effect every inherited function is overridden in the derived class. C++
enforces this behaviour through the abstract class.

15
Inheritance ‐ Abstract Classes
"An abstract class has no objects. An abstract class is written with the expectation that
its concrete derived classes will add to its structure and behaviour, typically by
implementing its abstract operations.“

A class is abstract if it contains one or more pure virtual functions. A pure virtual
function is a function that is declared virtual and has no definition.

virtual void Draw()=0; // A pure virtual function

It is not possible to create an object from an abstract class.

class TShape
{
virtual void Draw() = 0;
...
};
...
TShape Shape; // Compiler Error

TShape class is effectively saying that here is an interface Draw() for any object of type
TShape, but I have no idea how it should be implemented. Avoid the declaration of data
members within an abstract class as this provides some degree of implementation. 16
Thank You

Questions?

17

You might also like