Inheritance: hiding methods A derived class may use the base class’s member functions. A derived class may also redefine the base class’s member functions. In that case, the derived class’s definition hides the one in the base class.
Inheritance : hiding methods Example 1: class Base { public: Base() { } void print() {cout << "Base::print()\n";} }; class Derived : public Base { Derived() { } int main () { Derived obj; obj.print(); return 0; } Output: Base::print()
Inheritance : hiding methods Example 2: class Base { public: Base() { } void print() {cout << "Base::print()\n";} }; class Derived : public Base { Derived() { } void print() {cout << “Derived::print()\n";} int main () { Derived obj; obj.print(); return 0; } Output: Derived::print()
Inheritance : hiding methods Example 2: class Base { public: Base() { } void print() {cout << "Base::print()\n";} void print(int x) {cout << "Base::print(x)\n";} }; class Derived : public Base { Derived() { } void print() {cout << “Derived::print()\n";} int main () { Derived obj; obj.print(10); return 0; } print() is overloaded This definition hides BOTH Base::print methods. COMPILER ERROR! Base::print(x) is hidden by Derived::print()
Inheritance : hiding methods Example 2: class Base { public: Base() { } void print() {cout << "Base::print()\n";} void print(int x) {cout << "Base::print(x)\n";} }; class Derived : public Base { Derived() { } void print() {cout << “Derived::print()\n";} void print(int x) {cout << "Derived::print(x)\n";} int main () { Derived obj; obj.print(10); return 0; } Output: Derived::print(x)
Virtual functions Recall the class hierarchy: We wish to create an array of pointers to various subtypes of Vehicles. In addition, each member of the array should behave according to each subclass. This can be achieved if every Vehicle member function that is overriden in the subclasses is declared as virtual. Vehicle Truck Train Bicycle
Virtual functions Output: Example 1 fleet[0] is of type Vehicle! class Vehicle { public: Vehicle() { } void print() { cout << “Vehicle\n”;} }; class Train : public Vehicle { Train() { } void print() { cout << “Train\n”;} } class Truck : public Vehicle { Truck() { } void print() { cout << “Truck\n”;} int main () { Vehicle *fleet[2]; fleet[0] = new Train; fleet[0]->print(); fleet[1] = new Truck; fleet[1]->print(); delete fleet[0]; delete fleet[1]; return 0; } fleet[0] is of type Vehicle! Output: Vehicle
Virtual functions Output: Example 2 Now, the compiler will check class Vehicle { public: Vehicle() { } virtual void print() { cout << “Vehicle\n”;} }; class Train : public Vehicle { Train() { } virtual void print() { cout << “Train\n”;} } class Truck : public Vehicle { Truck() { } virtual void print() { cout << “Truck\n”;} int main () { Vehicle *fleet[2]; fleet[0] = new Train; fleet[0]->print(); fleet[1] = new Truck; fleet[1]->print(); delete fleet[0]; delete fleet[1]; return 0; } Now, the compiler will check the dynamic type of fleet[i] and call the appropriate method. Output: Train Truck
Virtual functions If a class has virtual methods, then the destructor must also be virtual! class Vehicle { public: Vehicle() { } ~Vehicle { } virtual void print() {}; }; class Train : public Vehicle { Train() { } ~Train { } } int main () { Vehicle *t = new Train; delete t; return 0; The sequence of constructor/destructor calls in this program is: Vehicle() Train() ~Vehicle() The Train destructor was never called, because it's not virtual, so the compiler called only the destructor for Vehicle, since this is the type of *t.
Virtual functions If a class has virtual methods, then the destructor must also be virtual! class Vehicle { public: Vehicle() { } virtual ~Vehicle { } virtual void print() {}; }; class Train : public Vehicle { Train() { } virtual ~Train { } } int main () { Vehicle *t = new Train; delete t; return 0; Now, the sequence of constructor/destructor calls is correct: Vehicle() Train() ~Train() ~Vehicle()
Multiple inheritance A class may inherit from more than one base class. JetCar inherits the members of Car and those of Jet. JetCar's constructor calls the constructors of Car and Jetin the order specified in JetCar's definition (i.e. as they appear after the colon) class Car { ... }; class Jet { ... }; class JetCar: public Car, public Jet{ ... };
Multiple inheritance What if we have: class Vehicle { ... }; JetCar's constructor calls the constructor of Car which calls the constructor of Vehicle and then calls the constructor of Jet which calls the constructor of Vehicle The Vehicle constructor was called twice! class Vehicle { ... }; class Car : public Vehicle { ... }; class Jet : public Vehicle { ... }; class JetCar: public Car, public Jet { ... };
Multiple inheritance Use virtual inheritance to tell the compiler that the base class constructor should be called only once: JetCar's constructor calls the constructor of Car which calls the constructor of Vehicle and then calls the constructor of Jet class Vehicle { ... }; class Car : virtual public Vehicle { ... }; class Jet : virtual public Vehicle { ... }; class JetCar: public Car, public Jet { ... };
Examples Most of the examples we did in class can be found in ~b11/labs/inheritance