For example, if we are implementing a game or a simulation we might have a variety of different types/classifications of vehicles:
In such a case, it would make sense to have the data and operations associated with the components also sit in a heirarchical framework.
For example, all vehicles might have some basic data and operations associated with them (current coordinates, facing, speed, weight, etc).
Then there might be additional properties/operations associated with all marine vehicles - which might be different than those associated with land vehicles etc.
Ideally we would like to define the properties and operations appropriate for each level or category of item, and to allow the more specialized versions to 'inherit' the properties and operations of the general classes they are a part of.
In the example above:
class vehicle { public: float x, y, z; // coordinates // method to print the coordinates void print(); // method to update the coordinates void update(); // default constructor and destructor vehicle(); ~vehicle(); };If we now want to create a new class, AirVehicle, and have it inherit everything from vehicles we can do so with the syntax below.
Note that in the new class we can add any new fields and methods desired, and can replace any of the vehicle methods with new versions.
class AirVehicle: public vehicle { public: // track the vehicle's air and ground speed float groundSpeed, airSpeed; // replace the vehicle print method with a specialized one void print(); // add a method to update the speeds void updateSpeed(); };If we create an AirVehicle object, it is possible to call either the AirVehicle print method, or its parent print method:
AirVehicle a; a.print(); // calls the air vehicle print a.vehicle::print(); // calls the parent version
It is also possible for the derived classes to specify how they will restrict access to the fields/methods they inherit. For instance, even though fields might have been public in the parent class, the derived class may wish to inherit them as protected or private.
The example below illustrates the different combinations of access permissions:
class parent { public: // fields accessible to anyone int x; protected: // fields accessible to parent and child methods int y; private: // fields accessible only to parent methods int z; }; class child1: public parent { // inherits x as public, y as protected, // cannot access z }; class child2: protected parent { // inherits x and y, treating them as protected, // cannot access z }; class child3: private parent { // inherits x and y, treating them as private, // cannot access z };
For example, if we have an AirVehicle class derived from a vehicle class and we create an object of type AirVehicle then the vehicle() constructor runs first, the AirVehicle() constructor second.
The reverse order applies to the destructors: e.g. ~AirVehicle() would run first, then ~vehicle().
In the example's output you can follow the sequence of constructor/destructor calls.
#include <iostream> using namespace std; class parent { public: parent(); ~parent(); void print(); private: char pdata; }; class child1: public parent { public: child1(); ~child1(); void print(); private: int cdata; }; class child2: public parent { public: child2(); child2(int c); ~child2(); void print(); private: int cdata; }; int main() { cout << "Child 1 default declaration" << endl; child1 C1; C1.print(); cout << endl; cout << "Child 2 default declaration" << endl; child2 C2; C2.print(); cout << endl; cout << "Child 2 override declaration" << endl; child2 C3(3); C3.print(); cout << endl; cout << "Now the destructors begin" << endl << endl; return 0; } | // parent implementations parent::parent() { cout << "Parent constructor" << endl; pdata = 'P'; } parent::~parent() { cout << "Parent destructor" << endl; } void parent::print() { cout << "Parent print " << pdata << endl; } // ------------------------------------- // child1 implementations child1::child1() { cdata = 1; cout << "child1 constructor " << cdata << endl; } child1::~child1() { cout << "child1 destructor " << cdata << endl; } void child1::print() { cout << "child1 print " << cdata << ":"; parent::print(); } // ------------------------------------- // child2 implementations // note the example here where the child constructor // explicitly identifies the parent constructor child2::child2():parent() { cdata = 2; cout << "child2 constructor " << cdata << endl; } // alternate child2 constructor child2::child2(int c) { cdata = c; cout << "child2 constructor " << cdata << endl; } child2::~child2() { cout << "child2 destructor " << cdata << endl; } void child2::print() { cout << "child2 print " << cdata << ":"; parent::print(); } |
//resulting output Child 1 default declaration Parent constructor child1 constructor 1 child1 print 1:Parent print P Child 2 default declaration Parent constructor child2 constructor 2 child2 print 2:Parent print P Child 2 override declaration Parent constructor child2 constructor 3 child2 print 3:Parent print P Now the destructors begin child2 destructor 3 Parent destructor child2 destructor 2 Parent destructor child1 destructor 1 Parent destructor |