Introduction to the second half of BTP200
Inheritance and Hierarchy
Derived Classes
Functions in a Hierarchy
Electrical Engineer by trade
+20 years of experience in Software Development
Teaching this course for the sixth time!
Brazilian, Canadian, and French
Father of two little honey badgers tasmanian devils beautiful children
→ Derived Classes
→ Virtual Functions
→ Function Templates
→ Polymorphism
→ Language Standards
→ Lectures will be in person
→ The addendum has been slightly changed
→ Workshops will also be in person - submission online is possible, but not recommended
→ Blackboard will be used for assignments, for announcements, and for grading purposes
→ Course's website contains general info and slides
→ Five Workshops (1%): In person labs
→ Five Quizzes (1.5%): Blackboard Tests
→ One Project (20% or 10%): In person presentation
→ One Final Exam (30% or 40%): in class pen-on-paper test
In OOP, the use of inheritance allows for code reusability
One class can inherit elements from another class and add its own extra elements
For example:
quadrilateral ← parallelogram ← rectangle ← square
Inheritance is naturally hierarchical
It ranks (orders) entities in an ordered structure
You can think of it as: (more general) ← (more specific)
A class lower in the hierarchy is a kind of class higher in the hierarchy
For example:
chordata ← mammal ← primate ← homo sapiens
If a class B inherits from a class A, it is said to be a derived class of A
In this case, A is said to be the base class for B
Note that a derived class can be the base class for another class
A derived class contains all variables (properties) and normal member functions of its base class
A derived class can add its own variables (properties) and normal member functions of its base class
A derived class does not inherit:
→ Constructors and destructors
→ Assignment operator
#include <iostream>
class Shape
{
protected:
float width_;
float height_;
public:
void set_width(int w)
{
width_ = w;
}
void set_height(int h)
{
height_ = h;
}
};
// Derived class
class Rectangle : public Shape
{
public:
int get_area()
{
return (width_ * height_);
}
};
// Derived class
class Triangle : public Shape
{
public:
int get_area()
{
return (width_ * height_ * 0.5);
}
};
int main(void)
{
Rectangle Rec;
Triangle Tri;
Rec.set_width(3);
Rec.set_height(4);
std::cout << "Area: " << Rec.get_area() << std::endl;
Tri.set_width(3);
Tri.set_height(4);
std::cout << "Area: " << Tri.get_area() << std::endl;
return 0;
}
C++ provides three levels of access for class member variables and functions:
private Acessible only within the class
protected Accessible within the class or within
derived classes
public Accessible anywhere
Granting protected access to a member variable exposes it
Hence, it is normally considered poor design
Consider providing getters and setters instead
There are exceptions, though:
→ The same team is working on both classes
→ The base class is provided as a template
A derived class does not inherits constructors and destructors from the base class
They have their own constructors and destructors
The constructors from a derived class calls the constructors of the base class before it starts
The destructor from a derived class calls the destructors of the base class after it ends
#include <iostream>
class Base
{
public:
Base()
{
std::cout << "Base class object was created!"
<< std::endl;
}
~Base()
{
std::cout << "Base class object was destroyed!"
<< std::endl;
}
};
class Derived : public Base
{
public:
Derived()
{
std::cout << "Derived class object was created!"
<< std::endl;
}
~Derived()
{
std::cout << "Derived class object was destroyed! "
<< std::endl;
}
};
int main(void)
{
Base baseObject;
Derived derivedObject;
return 0;
}
A derived class constructor can pass arguments to the base class constructor
The syntax is:
derivedClass(Type argumentName) : baseClass(argumentName)
#include <iostream>
#include <cstring>
const int N = 15;
class Base
{
protected:
char name_[N];
public:
Base()
{
name_[0] = '\0';
std::cout << "Base class object was created!"
<< std::endl;
}
Base(const char *argument)
{
strncpy(name_, argument, N);
name_[N - 1] = '\0';
std::cout << "Base class object was created for "
<< name_ << std::endl;
}
~Base()
{
std::cout << "Base class object was destroyed! "
<< "Bye " << name_ << std::endl;
}
};
class Derived : public Base
{
int age_;
public:
Derived()
{
std::cout << "Derived class object was created!"
<< std::endl;
}
Derived(const char *argument, int age) : Base(argument)
{
age_ = age;
std::cout << "Derived class object was created for "
<< name_ << std::endl;
}
~Derived()
{
std::cout << "Derived class object was destroyed! "
<< "Bye " << name_ << std::endl;
}
};
int main(void)
{
Base baseObject("Base Bill");
Derived derivedObject("Derived Bob", 25);
return 0;
}
A derived class can have member variables or functions with the same name as the ones in the base class
When this happens, the compiler binds a call the member function or variable from the derived class
We say that the member variable (or function) of the derived class shadows the one from the base class
#include <iostream>
#include <cstring>
const int N = 15;
class Person
{
protected:
char name_[N];
public:
Person()
{
name_[N - 1] = '\0';
}
Person(const char *argument)
{
strncpy(name_, argument, N);
name_[N - 1] = '\0';
}
void display() const
{
std::cout << name_ << "." << std::endl;
}
};
class Student : public Person
{
public:
Student(const char *argument) : Person(argument)
{
}
void display() const
{
std::cout << "Hi, I am a Student called ";
Person::display();
}
};
class Faculty : public Person
{
public:
Faculty(const char *argument) : Person(argument)
{
}
void display() const
{
std::cout << "Hi, I am a Faculty called ";
Person::display();
}
};
int main(void)
{
Student student("John Doe");
Faculty faculty("Jane Smith");
student.display();
faculty.display();
return 0;
}
It possible to access the base version of a shadowed identifier
For this, you have to use scope resolution
The syntax is:
baseClass::shadowedIdentifier(arguments)
#include <iostream>
#include <cstring>
const int N = 15;
class Person
{
protected:
char name_[N];
public:
Person()
{
name_[0] = '\0';
}
Person(const char *argument)
{
strncpy(name_, argument, N);
name_[N - 1] = '\0';
}
void display() const
{
std::cout << "Hi, I am a Person called "
<< name_ << "." << std::endl;
}
};
class Student : public Person
{
public:
Student(const char *argument) : Person(argument)
{
}
void display() const
{
std::cout << "Hi, I am a Student called "
<< name_ << "." << std::endl;
}
void display_from_base() const
{
Person::display();
}
};
class Faculty : public Person
{
public:
Faculty(const char *argument) : Person(argument)
{
}
void display() const
{
std::cout << "Hi, I am a Faculty called "
<< name_ << "." << std::endl;
}
};
int main(void)
{
Person person("Base Bill");
Student student("John Doe");
Faculty faculty("Jane Smith");
person.display();
student.display();
student.display_from_base();
faculty.display();
return 0;
}