Week 10 Lecture Summary for CSC 309
These notes are not intended to be complete. They serve as an outline to the class and as a supplement to the text.
Polymorphism
- Polymorphism means many forms.
- In an Object-Oriented context, polymorphism implies a single method name to be used in many ways.
- Overloading
When a single method name is defined more than once in the same class but with different parameter lists.
Example
The vector class has two methods named insert. Let v be a vector object
v.insert(it, t) // inserts value t before the element pointed to by iterator it. v.insert(it n, t) // inserts n elements of t before it.
- There must be an inheritance hierarchy.
- The classes in the hierarchy must have a virtual method with the same signature.
- There must be a pointer or a reference to a base class variable. The pointer or reference is used to invoke a virtual method.
Figure 7.1.2 from the Text
#include <iostream>
using namespace std;
class TradesPerson
{
public:
virtual void sayHi() { cout << "Hi from on top." << endl; }
};
class Tinker : public TradesPerson
{
public:
virtual void sayHi() { cout << "Hi, I tinker." << endl; }
};
class Tailor : public TradesPerson
{
public:
virtual void sayHi() { cout << "Hi, I tailor." << endl;}
};
int main(int argc, char *argv[])
{
TradesPerson *p; // Pointer to base class
int which;
do {
cout << "Enter 1 for TradesPerson, 2 for Tinker, 3 for Tailor" << endl;
cin >> which;
}while( which < 1 || which > 3);
switch(which){
case 1: p = new TradesPerson; break;
case 2: p = new Tinker; break;
case 3: p = new Tailor; break;
}
p -> sayHi(); // run time binding
delete p;
system("PAUSE");
return EXIT_SUCCESS;
}
- Only methods can be declared virtual. Global function cannot be virtual.
- The keyword virtual does not need to be repeated in the derived classes.
- If a virtual method is defined outside the class, then the keyword virtual occurs only in the methods declaration, not the definition.
- The following would be syntax errors.
void virtual Tinker::sayHi() {
{ cout << "Hi, I tinker." << endl; }
}
virtual void Tinker::sayHi() {
{ cout << "Hi, I tinker." << endl; }
}
- A Virtual method can be inherited by a derived class.
Example
class TradesPerson
{
public:
virtual void sayHi() { cout << "Hi from on top." << endl; }
};
class Tinker : public TradesPerson
{
public:
};
class Tailor : public TradesPerson
{
public:
virtual void sayHi() { cout << "Hi, I tailor." << endl;}
};
TradesPerson *tr = new Tinker;
tr -> sayHi(); // output Hi from on top
tr = new Tailor;
tr -> sayHi(); // output Hi, I tailor.
- Class Tailor overrides virtual method
sayHi. - Class Tinker does not override
sayHibut inherits the one from Tailor.
Vtable
- C++ uses a vtable to implement the run-time binding of virtual methods.
- Provides a lookup table that allows the system to bind a function's name to a particular entry point
- An entry point is the starting address at which a functions code resides.
- Given the inheritance hierarchy
class B
{
public:
virtual void m1() { ...... } // 1st virtual method
virtual void m2() { ...... } // 2nd virtual method
};
class D : public B
{
public:
virtual void m1() { ...... } // overrides 1rst virtual method
};
- Conceptual representation of vtable
- Run-time binding incurs a performance penalty.
- Need extra storage space for vtable.
- Time it takes to do the look up.
| Virtual Method | Sample Entry Point |
|---|---|
| B::m1 | 0x7723 |
| B::m2 | 0x23b4 |
| D::m1 | 0x99a7 |
| D::m2 | 0x23b4 |
Constructors and Destructors
- A constructor cannot be virtual.
- A destructor can be virtual.
- The order of destruction is reversed from construction.
- Should make destructors virtual if your classes will be extended.
- Virtual destructors are exceptions to the rule of three.
Example
class Base
{
public:
Base() { cout << "Base Constructor" << endl; }
~Base() { cout << "Base Destructor" << endl; }
};
class Derived : public Base
{
public:
Derived() { cout << "Derived Constructor" << endl;}
~Derived() { cout << "Derived Destructor"<< endl;}
};
int main(){
Base *obj = new Derived();
delete obj;
system("pause");
return 0;
}
Output
---------
Base Constructor
Derived Constructor
Base Destructor
Problem if derived class dynamically allocates memory it's destructor never gets called. Solution make destructor virtual.
class Base
{
public:
Base() { cout << "Base Constructor" << endl; }
virtual ~Base() { cout << "Base Destructor" << endl; }
};
class Derived : public Base
{
public:
Derived() { cout << "Derived Constructor" << endl; }
~Derived() { cout << "Derived Destructor" << endl; }
};
int main(){
Base *obj = new Derived();
delete obj;
system("pause");
return 0;
}
Output
-------
Base Constructor
Derived Constructor
Derived Destructor
Base Destructor
See destruct1.cpp, destruct2.cpp, destruct3.cpp
Abstract Base Classes.
- An abstract class cannot be instantiated.
- To make a class abstract it must have a pure virtual method.
- A pure virtual method is one initialized to zero in its declaration.
class Employee
{
public:
virtual void pay() = 0;
};
Employee emp; // error cannot be instantiated
- The intialization of
payto zero is a syntactic requirement to makepaypure virtual - Cannot assign a value to methods or global functions.
- An abstract class can contain non virtual methods and / or instance variables.
class Employee
{
private:
string name;
string id;
string phone;
public:
Employee(string n, string i, string p) :
name(n), id(i), phone(p) {}
Employee() {}
string getName() const { return name; }
string getId() const { return id; }
string getPhone() const { return phone; }
void setName(string n) { name = n; }
void setId(string i) { id = i; }
void setPhone (string p ) { phone = p; }
virtual ~Employee() {}
virtual double pay() = 0;
};
- An abstract base class is used to factor out commonality between related class.
- The abstract class serves as a Base class in a inheritance hierarchy.
- A class derived from an abstract class must override all of the base class's pure virtual methods.
class HourlyEmployee : public Employee
{
private:
double rate;
double hrs;
public:
HourlyEmployee(string n, string i, string p, double r, double h) :
Employee(n,i,p), rate(r), hrs(h) {}
HourlyEmployee () {}
virtual double pay() { return rate * hrs; }
};
- Class
HourlyEmployeeoverrides pure virtual methodpayfrom abstract classEmployee - Class
HourlyEmployeeis not abstract therefore, it may instantiate objects.
HourlyEmployee emp1("Rita","111","5553434", 25, 20);
cout << emp1.getName() << emp1.pay() << endl;
Employee *emp2 = new HourlyEmployee("Tom","123","5551212", 15, 10);
cout << emp2 -> getName() << emp2 -> pay() << endl;
See payday1.cpp and payday2.cpp
STL multimap Class
- A
multimapallows a key to have multiple values. - Name -- > multiple phone numbers
- Online user --> multiple friends
multimapis defined in themapheader.#include <map>- Similar operations as a
map - No subscript operator.
[ ]
Creating a multimap
Consider keeping track of words and the line numbers they appear on.
#include <map> using namespace std; multimap<string, int> index;
Inserting Elements into a multimap
index.insert(make_pair("transform" , 56));
index.insert(make_pair("transform" , 121));
index.insert(make_pair("transform" , 141));
index.insert(make_pair("virtual", 56));
index.insert(make_pair("virtual", 59));
Traversing a multimap
for(multimap<string, int>::iterator it = index.begin(); it != index.end(); ++it){
cout << it -> first << " " << it -> second << endl;
}
Finding values based on key.
(Method 1)
- The entries with the same key will be adjacent withing the multimap.
- Method
countreturns the number of times a given key occurs. - Method
findreturns and iterator to the first occurrence of the key.
int countKey = index.count("transform");
multimap<string, int>::iterator itKey = index.find("transform");
for(int i = 0; i != countKey; ++i, ++itKey){
cout << itKey -> second << " " << endl;
}
(Method 2)
- Using methods
lower_boundandupper_bound. lower_boundreturns an iterator to the first entry for a given key.upper_boundreturns an iterator just after the last occurrence of the key
multimap<string, int>::iterator beg = index.lower_bound("transform");
multimap<string, int>::iterator end = index.upper_bound("transform");
for(; beg != end; beg++){
cout << beg -> second << " " << endl;
}
Remove
index.erase("transform");
- The above removes all entries with key transform.
- To remove a specific value for a given key use oveloaded
erase - erase also will remove the entry pointed at by an iterator.
- To remove the entry tramsform -- > 121.
multimap<string, int>::iterator beg, end;
beg = index.lower_bound("transform");
end = index.upper_bound("transform");
for(; beg != end; beg++){
if(beg -> second == 121){
index.erase(beg);
}
}