1 / 16

Inheritance

Inheritance. We are modeling the operation of a transportation company that uses trains and trucks to transfer goods. A suitable class hierarchy for the vehicles is: We will store all the vehicle information in array.

don
Télécharger la présentation

Inheritance

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Inheritance • We are modeling the operation of a transportation company that uses trains and trucks to transfer goods. A suitable class hierarchy for the vehicles is: • We will store all the vehicle information in array. • Create an array of n vehicle objects. Some will be trains, and the rest will be trucks. How do we do it? Vehicle Train Truck

  2. Vehicle hierarchy, take 1 class Vehicle { public: Vehicle() { } void print() { cout << “Vehicle\n”;} }; class Train : public Vehicle { public: Train() { } void print() { cout << “Train\n”;} }; class Truck : public Vehicle { public: 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 Vehicle

  3. Inheritance & virtual functions Vehicle *fleet[2]; fleet[0] = new Train; fleet[1] = new Truck; • A Train is a Vehicle, so the syntax fleet[0] = new Train;works, but ultimately, *fleet[0] is a Vehicle object, so we cannot apply any Train methods to it. • We would like our program to have the capability to determine the type of an object (e.g. *fleet[0]) at execution time. • There is such a mechanism and it's called RTTI(Run-Time Type Information) • We will use virtual functions which allow the program to choose the derived class function, dynamically, instead of the base class function.

  4. Vehicle hierarchy, take 2 class Vehicle { public: Vehicle() { } virtual void print() { cout << “Vehicle\n”; } }; class Train : public Vehicle { public: Train() { } virtual void print() { cout << “Train\n”; } }; class Truck : public Vehicle { public: Truck() { } virtual void print() { cout << “Truck\n”; } }; int main () { Vehicle *fleet[2]; int vehicle_type; for (int i=0; i<2; i++) { cout << "Enter 1 for Train,2 for Truck"; cin >> vehicle_type; if (vehicle_type == 1) fleet[i] = new Train; else fleet[i] = new Truck; fleet[0]->print(); fleet[1]->print(); delete fleet[0]; delete fleet[1]; return 0; } The dynamic types of *fleet[0] and *fleet[1] are determined at runtime. The appropriate method is called based on the dynamic type of fleet[i]. For input 1 2 the output is: Train Truck

  5. 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 { public: Train() { } ~Train { } virtual void print() {}; } ; 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! The Vehicle destructor was called because this is the type of *t.

  6. 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 { public: Train() { } virtual ~Train { } virtual void print() {}; } ; int main () { Vehicle *t = new Train; delete t; return 0; } Now, the sequence of constructor/destructor calls is correct: Vehicle() Train() ~Train() ~Vehicle()

  7. Assignments class Base { ... }; class Derived : public Base {...}; Base b, *pb; Derived der, *pder; • der = b; // ERROR! A Base object is not always a Derived object. // Note: what values will be given to the "extra" data // members of the Derived object? • pder = pb // ERROR! Same reason as above.

  8. Assignments class Base { ... }; class Derived : public Base {...}; Base b, *pb; Derived der, *pder; • b = der; // OK! A Derived object is also a Base object • pb = pder; // Mostly OK! • pb->basefunc() will work (basefunc() is a Base member function) • pb->derivedfunc() will give a compiler ERROR (derivedfunc() is a Derived member function)

  9. Assignments class Base { ... }; class Derived : public Base {...}; Base b, *pb; Derived der, *pder; • pb = new Derived; pder = pb ; // ERROR! • pb = new Derived;pder = (Derived*) pb ; // Dangerous! We are trying to downcast! • However, there is a case when we may have to downcast.

  10. Downcasting • We'd like to do this: int main () { Vehicle *fleet[2]; int vehicle_type; for (int i=0; i<2; i++) { cout << "Enter 1 for Train,2 for Truck"; cin >> vehicle_type; if (vehicle_type == 1) fleet[0] = new Train; else fleet[1] = new Truck; for (int i=0; i<2; i++) { if *fleet[i] is a train, do A else, do B } ... How can we check the dynamic type of *fleet[i] at runtime? • Trick: • Create a Train pointer and assign fleet[i] to it. • Use the dynamic_cast method to perform this downcasting (from Vehicle* to Train*) • If fleet[i]'s dynamic type is also Train*, then the casting will succeed. • If it's not (i.e. it's a Truck* instead), then the casting fails, and the pointer is NULL.

  11. Downcasting int main () { Vehicle *fleet[2]; int vehicle_type; for (int i=0; i<2; i++) { cout << "Enter 1 for Train,2 for Truck"; cin >> vehicle_type; if (vehicle_type == 1) fleet[0] = new Train; else fleet[1] = new Truck; for (int i=0; i<2; i++) { Train *tr = dynamic_cast<Train *> ( fleet[i] ); if (tr != NULL) // it means that fleet[i] was indeed a train // do A, else // do B. ...

  12. 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{ ... };

  13. Multiple inheritance • What if we have: • 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 { ... };

  14. 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 { ... };

  15. Abstract classes • Since the company has either trucks or trains, we would like to disallow the creation of Vehicle objects. • We want to force the compiler to give an error in the following case: • In other words, we want to make Vehicle an abstract class. • This will make Train and Truck concrete classes. • To make a class as abstract, we declare one of its virtual methods to be "pure", by placing a =0 at the end of its declaration. Vehicle *v; v = new Vehicle; // ERROR: We should only create // a train or a truck!

  16. Abstract classes class Vehicle { public: Vehicle() { } virtual void print() = 0; }; class Train : public Vehicle { public: Train() { } void print() { cout << “Train\n”;} }; class Truck : public Vehicle { public: Truck() { } void print() { cout << “Truck\n”;} }; • Pure virtual functions typically do not have an implementation. • Every concrete derived class MUST override all base class pure virtual functions. • Even though we can't instantiate abstract class objects, we can still use the abstract class to create pointers. • Vehicle *v; // OK • Vehicle *v = new Train; // OK • Vehcile *v = new Vehicle // ERROR!

More Related