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
sayHi
but 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
pay
to zero is a syntactic requirement to makepay
pure 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
HourlyEmployee
overrides pure virtual methodpay
from abstract classEmployee
- Class
HourlyEmployee
is 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
multimap
allows a key to have multiple values. - Name -- > multiple phone numbers
- Online user --> multiple friends
multimap
is defined in themap
header.#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
count
returns the number of times a given key occurs. - Method
find
returns 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_bound
andupper_bound
. lower_bound
returns an iterator to the first entry for a given key.upper_bound
returns 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); } }