// Inheritance & upcasting
enum note { middleC, Csharp, Cflat }; // Etc.
class Instrument {
public:
void play(note) const {}
};
// Wind objects are Instruments
// because they have the same interface:
class Wind : public Instrument {};
void tune(Instrument& i) {
// ...
i.play(middleC);
}
int main() {
Wind flute;
tune(flute); // Upcasting
} ///:~
Inheritance Compiler Support
// Correctly creating the copy-constructor
#include <iostream>
using namespace std;
class Parent {
int i;
public:
Parent(int ii) : i(ii) {
cout << "Parent(int ii)n";
}
Parent(const Parent& b) : i(b.i) {
cout << "Parent(const Parent&)n";
}
Parent() : i(0) { cout << "Parent()n"; }
friend ostream&
operator<<(ostream& os, const Parent& b) {
return os << "Parent: " << b.i << endl;
}
};
Parameter Passing & Upcasting
class Member {
int i;
public:
Member(int ii) : i(ii) {
cout << "Member(int ii)n";
}
Member(const Member& m) : i(m.i) {
cout << "Member(const Member&)n";
}
friend ostream&
operator<<(ostream& os, const Member& m) {
return os << "Member: " << m.i << endl;
}
};
Parameter Passing & Upcasting
class Child : public Parent {
int i;
Member m;
public:
Child(int ii) : Parent(ii), i(ii), m(ii) {
cout << "Child(int ii)n";
}
friend ostream&
operator<<(ostream& os, const Child& c){
return os << (Parent&)c << c.m
<< "Child: " << c.i << endl;
}
};
int main() {
Child c(2);
cout << "calling copy-constructor: " << endl;
Child c2 = c; // Calls copy-constructor
cout << "values in c2:n" << c2;
} ///:~
Parameter Passing & Upcasting
Wind w;
Instrument* ip = &w; // Upcast
Instrument& ir = w; // Upcast
Pointer & Reference Upcasting
// Pure virtual base definitions
#include <iostream>
using namespace std;
class Pet {
public:
virtual void speak() const = 0;
virtual void eat() const = 0;
// Inline pure virtual definitions illegal:
//! virtual void sleep() const = 0 {}
};
// OK, not defined inline
void Pet::eat() const {
cout << "Pet::eat()" << endl;
}
void Pet::speak() const {
cout << "Pet::speak()" << endl;
}
Pure Virtual Definitions
class Dog : public Pet {
public:
// Use the common Pet code:
void speak() const { Pet::speak(); }
void eat() const { Pet::eat(); }
};
int main() {
Dog simba; // Richard's dog
simba.speak();
simba.eat();
} ///:~
Pure Virtual Definitions
//: C15:DynamicCast.cpp
#include <iostream>
using namespace std;
class Pet { public: virtual ~Pet(){}};
class Dog : public Pet {};
class Cat : public Pet {};
int main() {
Pet* b = new Cat; // Upcast
// Try to cast it to Dog*:
Dog* d1 = dynamic_cast<Dog*>(b);
// Try to cast it to Cat*:
Cat* d2 = dynamic_cast<Cat*>(b);
cout << "d1 = " << (long)d1 << endl;
cout << "d2 = " << (long)d2 << endl;
} ///:~
Downcasting: dynamic_cast
// Navigating class hierarchies with static_cast
#include <iostream>
#include <typeinfo>
using namespace std;
class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};
int main() {
Circle c;
Shape* s = &c; // Upcast: normal and OK
// More explicit but unnecessary:
s = static_cast<Shape*>(&c);
// (Since upcasting is such a safe and common
// operation, the cast becomes cluttering)
Circle* cp = 0;
Square* sp = 0;
RTTI
// Static Navigation of class hierarchies
// requires extra type information:
if(typeid(s) == typeid(cp)) // C++ RTTI
cp = static_cast<Circle*>(s);
if(typeid(s) == typeid(sp))
sp = static_cast<Square*>(s);
if(cp != 0)
cout << "It's a circle!" << endl;
if(sp != 0)
cout << "It's a square!" << endl;
// Static navigation is ONLY an efficiency hack;
// dynamic_cast is always safer. However:
// Other* op = static_cast<Other*>(s);
// Conveniently gives an error message, while
Other* op2 = (Other*)s;
// does not
} ///:~
RTTI
// Returning a pointer or reference to a derived
// type during overriding
#include <iostream>
#include <string>
using namespace std;
class PetFood {
public:
virtual string foodType() const = 0;
};
class Pet {
public:
virtual string type() const = 0;
virtual PetFood* eats() = 0;
};
Variant Return Type
class Bird : public Pet {
public:
string type() const { return "Bird"; }
class BirdFood : public PetFood {
public:
string foodType() const {
return "Bird food";
}
};
// Upcast to base type:
PetFood* eats() { return &bf; }
private:
BirdFood bf;
};
Variant Return Type
class Cat : public Pet {
public:
string type() const { return "Cat"; }
class CatFood : public PetFood {
public:
string foodType() const { return "Birds"; }
};
// Return exact type instead:
CatFood* eats() { return &cf; }
private:
CatFood cf;
};
Variant Return Type
int main() {
Bird b;
Cat c;
Pet* p[] = { &b, &c, };
for(int i = 0; i < sizeof p / sizeof *p; i++)
cout << p[i]->type() << " eats “<< p[i]->eats()->foodType() << endl;
// Can return the exact type:
Cat::CatFood* cf = c.eats();
Bird::BirdFood* bf;
// Cannot return the exact type:
//! bf = b.eats();
// Must downcast:
bf = dynamic_cast<Bird::BirdFood*>(b.eats());
} ///:~
Variant Return Type
// Behavior of virtual vs. non-virtual destructor
#include <iostream>
using namespace std;
class Base1 {
public:
~Base1() { cout << "~Base1()n"; }
};
class Derived1 : public Base1 {
public:
~Derived1() { cout << "~Derived1()n"; }
};
class Base2 {
public:
virtual ~Base2() { cout << "~Base2()n"; }
};
class Derived2 : public Base2 {
public:
~Derived2() { cout << "~Derived2()n"; }
};
Virtual Destructors
int main() {
Base1* bp = new Derived1; // Upcast
delete bp;
Base2* b2p = new Derived2; // Upcast
delete b2p;
} ///:~
Virtual Destructors
// Pure virtual destructors
// seem to behave strangely
class AbstractBase {
public:
virtual ~AbstractBase() = 0;
};
AbstractBase::~AbstractBase() {}
class Derived : public AbstractBase {};
// No overriding of destructor necessary?
int main() { Derived d; } ///:~
Pure Virtual Destructors
// Pure virtual destructors require a function body
class Pet {
public:
virtual ~Pet() = 0;
};
Pet::~Pet() {
cout << "~Pet()" << endl;
}
class Dog : public Pet {
public:
~Dog() {
cout << "~Dog()" << endl;
}
};
int main() {
Pet* p = new Dog; // Upcast
delete p; // Virtual destructor call
} ///:~
Pure Virtual Destructors
// Virtual calls inside destructors
class Base {
public:
virtual ~Base() {
cout << "Base1()n";
f();
}
virtual void f() { cout << "Base::f()n"; }
};
class Derived : public Base {
public:
~Derived() { cout << "~Derived()n"; }
void f() { cout << "Derived::f()n"; }
};
int main() {
Base* bp = new Derived; // Upcast
delete bp;
} ///:~
Virtual Function Call in
Destructors
Same Behaviour
seen in Constructors
// Polymorphism with overloaded operators
#include <iostream>
using namespace std;
class Matrix;
class Scalar;
class Vector;
class Math {
public:
virtual Math& operator*(Math& rv) = 0;
virtual Math& multiply(Matrix*) = 0;
virtual Math& multiply(Scalar*) = 0;
virtual Math& multiply(Vector*) = 0;
virtual ~Math() {}
};
Operator Overloading: Virtual
class Matrix : public Math {
public:
Math& operator*(Math& rv) {
return rv.multiply(this); // 2nd dispatch
}
Math& multiply(Matrix*) {
cout << "Matrix * Matrix" << endl;
return *this;
}
Math& multiply(Scalar*) {
cout << "Scalar * Matrix" << endl;
return *this;
}
Math& multiply(Vector*) {
cout << "Vector * Matrix" << endl;
return *this;
}
};
Operator Overloading: Virtual
class Scalar : public Math {
public:
Math& operator*(Math& rv) {
return rv.multiply(this); // 2nd dispatch
}
Math& multiply(Matrix*) {
cout << "Matrix * Scalar" << endl;
return *this;
}
Math& multiply(Scalar*) {
cout << "Scalar * Scalar" << endl;
15: Polymorphism & Virtual Functions 711
return *this;
}
Math& multiply(Vector*) {
cout << "Vector * Scalar" << endl;
return *this;
}
};
Operator Overloading: Virtual
class Vector : public Math {
public:
Math& operator*(Math& rv) {
return rv.multiply(this); // 2nd dispatch
}
Math& multiply(Matrix*) {
cout << "Matrix * Vector" << endl;
return *this;
}
Math& multiply(Scalar*) {
cout << "Scalar * Vector" << endl;
return *this;
}
Math& multiply(Vector*) {
cout << "Vector * Vector" << endl;
return *this;
}
};
Operator Overloading: Virtual
int main() {
Matrix m; Vector v; Scalar s;
Math* math[] = { &m, &v, &s };
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++) {
Math& m1 = *math[i];
Math& m2 = *math[j];
m1 * m2;
}
} ///:~
Operator Overloading: Virtual

Inheritance compiler support

  • 1.
    // Inheritance &upcasting enum note { middleC, Csharp, Cflat }; // Etc. class Instrument { public: void play(note) const {} }; // Wind objects are Instruments // because they have the same interface: class Wind : public Instrument {}; void tune(Instrument& i) { // ... i.play(middleC); } int main() { Wind flute; tune(flute); // Upcasting } ///:~ Inheritance Compiler Support
  • 2.
    // Correctly creatingthe copy-constructor #include <iostream> using namespace std; class Parent { int i; public: Parent(int ii) : i(ii) { cout << "Parent(int ii)n"; } Parent(const Parent& b) : i(b.i) { cout << "Parent(const Parent&)n"; } Parent() : i(0) { cout << "Parent()n"; } friend ostream& operator<<(ostream& os, const Parent& b) { return os << "Parent: " << b.i << endl; } }; Parameter Passing & Upcasting
  • 3.
    class Member { inti; public: Member(int ii) : i(ii) { cout << "Member(int ii)n"; } Member(const Member& m) : i(m.i) { cout << "Member(const Member&)n"; } friend ostream& operator<<(ostream& os, const Member& m) { return os << "Member: " << m.i << endl; } }; Parameter Passing & Upcasting
  • 4.
    class Child :public Parent { int i; Member m; public: Child(int ii) : Parent(ii), i(ii), m(ii) { cout << "Child(int ii)n"; } friend ostream& operator<<(ostream& os, const Child& c){ return os << (Parent&)c << c.m << "Child: " << c.i << endl; } }; int main() { Child c(2); cout << "calling copy-constructor: " << endl; Child c2 = c; // Calls copy-constructor cout << "values in c2:n" << c2; } ///:~ Parameter Passing & Upcasting
  • 5.
    Wind w; Instrument* ip= &w; // Upcast Instrument& ir = w; // Upcast Pointer & Reference Upcasting
  • 6.
    // Pure virtualbase definitions #include <iostream> using namespace std; class Pet { public: virtual void speak() const = 0; virtual void eat() const = 0; // Inline pure virtual definitions illegal: //! virtual void sleep() const = 0 {} }; // OK, not defined inline void Pet::eat() const { cout << "Pet::eat()" << endl; } void Pet::speak() const { cout << "Pet::speak()" << endl; } Pure Virtual Definitions
  • 7.
    class Dog :public Pet { public: // Use the common Pet code: void speak() const { Pet::speak(); } void eat() const { Pet::eat(); } }; int main() { Dog simba; // Richard's dog simba.speak(); simba.eat(); } ///:~ Pure Virtual Definitions
  • 8.
    //: C15:DynamicCast.cpp #include <iostream> usingnamespace std; class Pet { public: virtual ~Pet(){}}; class Dog : public Pet {}; class Cat : public Pet {}; int main() { Pet* b = new Cat; // Upcast // Try to cast it to Dog*: Dog* d1 = dynamic_cast<Dog*>(b); // Try to cast it to Cat*: Cat* d2 = dynamic_cast<Cat*>(b); cout << "d1 = " << (long)d1 << endl; cout << "d2 = " << (long)d2 << endl; } ///:~ Downcasting: dynamic_cast
  • 9.
    // Navigating classhierarchies with static_cast #include <iostream> #include <typeinfo> using namespace std; class Shape { public: virtual ~Shape() {}; }; class Circle : public Shape {}; class Square : public Shape {}; class Other {}; int main() { Circle c; Shape* s = &c; // Upcast: normal and OK // More explicit but unnecessary: s = static_cast<Shape*>(&c); // (Since upcasting is such a safe and common // operation, the cast becomes cluttering) Circle* cp = 0; Square* sp = 0; RTTI
  • 10.
    // Static Navigationof class hierarchies // requires extra type information: if(typeid(s) == typeid(cp)) // C++ RTTI cp = static_cast<Circle*>(s); if(typeid(s) == typeid(sp)) sp = static_cast<Square*>(s); if(cp != 0) cout << "It's a circle!" << endl; if(sp != 0) cout << "It's a square!" << endl; // Static navigation is ONLY an efficiency hack; // dynamic_cast is always safer. However: // Other* op = static_cast<Other*>(s); // Conveniently gives an error message, while Other* op2 = (Other*)s; // does not } ///:~ RTTI
  • 11.
    // Returning apointer or reference to a derived // type during overriding #include <iostream> #include <string> using namespace std; class PetFood { public: virtual string foodType() const = 0; }; class Pet { public: virtual string type() const = 0; virtual PetFood* eats() = 0; }; Variant Return Type
  • 12.
    class Bird :public Pet { public: string type() const { return "Bird"; } class BirdFood : public PetFood { public: string foodType() const { return "Bird food"; } }; // Upcast to base type: PetFood* eats() { return &bf; } private: BirdFood bf; }; Variant Return Type
  • 13.
    class Cat :public Pet { public: string type() const { return "Cat"; } class CatFood : public PetFood { public: string foodType() const { return "Birds"; } }; // Return exact type instead: CatFood* eats() { return &cf; } private: CatFood cf; }; Variant Return Type
  • 14.
    int main() { Birdb; Cat c; Pet* p[] = { &b, &c, }; for(int i = 0; i < sizeof p / sizeof *p; i++) cout << p[i]->type() << " eats “<< p[i]->eats()->foodType() << endl; // Can return the exact type: Cat::CatFood* cf = c.eats(); Bird::BirdFood* bf; // Cannot return the exact type: //! bf = b.eats(); // Must downcast: bf = dynamic_cast<Bird::BirdFood*>(b.eats()); } ///:~ Variant Return Type
  • 15.
    // Behavior ofvirtual vs. non-virtual destructor #include <iostream> using namespace std; class Base1 { public: ~Base1() { cout << "~Base1()n"; } }; class Derived1 : public Base1 { public: ~Derived1() { cout << "~Derived1()n"; } }; class Base2 { public: virtual ~Base2() { cout << "~Base2()n"; } }; class Derived2 : public Base2 { public: ~Derived2() { cout << "~Derived2()n"; } }; Virtual Destructors
  • 16.
    int main() { Base1*bp = new Derived1; // Upcast delete bp; Base2* b2p = new Derived2; // Upcast delete b2p; } ///:~ Virtual Destructors
  • 17.
    // Pure virtualdestructors // seem to behave strangely class AbstractBase { public: virtual ~AbstractBase() = 0; }; AbstractBase::~AbstractBase() {} class Derived : public AbstractBase {}; // No overriding of destructor necessary? int main() { Derived d; } ///:~ Pure Virtual Destructors
  • 18.
    // Pure virtualdestructors require a function body class Pet { public: virtual ~Pet() = 0; }; Pet::~Pet() { cout << "~Pet()" << endl; } class Dog : public Pet { public: ~Dog() { cout << "~Dog()" << endl; } }; int main() { Pet* p = new Dog; // Upcast delete p; // Virtual destructor call } ///:~ Pure Virtual Destructors
  • 19.
    // Virtual callsinside destructors class Base { public: virtual ~Base() { cout << "Base1()n"; f(); } virtual void f() { cout << "Base::f()n"; } }; class Derived : public Base { public: ~Derived() { cout << "~Derived()n"; } void f() { cout << "Derived::f()n"; } }; int main() { Base* bp = new Derived; // Upcast delete bp; } ///:~ Virtual Function Call in Destructors Same Behaviour seen in Constructors
  • 20.
    // Polymorphism withoverloaded operators #include <iostream> using namespace std; class Matrix; class Scalar; class Vector; class Math { public: virtual Math& operator*(Math& rv) = 0; virtual Math& multiply(Matrix*) = 0; virtual Math& multiply(Scalar*) = 0; virtual Math& multiply(Vector*) = 0; virtual ~Math() {} }; Operator Overloading: Virtual
  • 21.
    class Matrix :public Math { public: Math& operator*(Math& rv) { return rv.multiply(this); // 2nd dispatch } Math& multiply(Matrix*) { cout << "Matrix * Matrix" << endl; return *this; } Math& multiply(Scalar*) { cout << "Scalar * Matrix" << endl; return *this; } Math& multiply(Vector*) { cout << "Vector * Matrix" << endl; return *this; } }; Operator Overloading: Virtual
  • 22.
    class Scalar :public Math { public: Math& operator*(Math& rv) { return rv.multiply(this); // 2nd dispatch } Math& multiply(Matrix*) { cout << "Matrix * Scalar" << endl; return *this; } Math& multiply(Scalar*) { cout << "Scalar * Scalar" << endl; 15: Polymorphism & Virtual Functions 711 return *this; } Math& multiply(Vector*) { cout << "Vector * Scalar" << endl; return *this; } }; Operator Overloading: Virtual
  • 23.
    class Vector :public Math { public: Math& operator*(Math& rv) { return rv.multiply(this); // 2nd dispatch } Math& multiply(Matrix*) { cout << "Matrix * Vector" << endl; return *this; } Math& multiply(Scalar*) { cout << "Scalar * Vector" << endl; return *this; } Math& multiply(Vector*) { cout << "Vector * Vector" << endl; return *this; } }; Operator Overloading: Virtual
  • 24.
    int main() { Matrixm; Vector v; Scalar s; Math* math[] = { &m, &v, &s }; for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) { Math& m1 = *math[i]; Math& m2 = *math[j]; m1 * m2; } } ///:~ Operator Overloading: Virtual

Editor's Notes

  • #11 Not out of hierarchy: Safer than typical cast
  • #12 Not out of hierarchy: Safer than typical cast
  • #13 Not out of hierarchy: Safer than typical cast
  • #14 Not out of hierarchy: Safer than typical cast
  • #15 Not out of hierarchy: Safer than typical cast
  • #16 Not out of hierarchy: Safer than typical cast
  • #17 Not out of hierarchy: Safer than typical cast
  • #18 Not out of hierarchy: Safer than typical cast
  • #19 Not out of hierarchy: Safer than typical cast
  • #20 Not out of hierarchy: Safer than typical cast
  • #21 Not out of hierarchy: Safer than typical cast
  • #22 Not out of hierarchy: Safer than typical cast
  • #23 Not out of hierarchy: Safer than typical cast
  • #24 Not out of hierarchy: Safer than typical cast
  • #25 Not out of hierarchy: Safer than typical cast