// 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