Inheritance ----------- - Inheritance allows grouping classes into families of related types. - Allows sharing of common operations and data. - C++ allows programing these families as a unit. - Rather than as individual classes. - Consider a windowing system example Window | |- background_color |- font |- parent (hierarchy for iconifying) |- draw (update window) |- clear -------------------------------------------------------- (no parent windows) | | | | | Frame Window Drawing Area Menubar | | | |- title |- draw_pixel |- add_menu |- iconify |- draw_line |- re_size |- draw_polygon | |- draw (OVERRIDEs the draw in base) Dialog | |- add_button |- get_response - Inheritance defines a parent/child relationship. - The parent is called the base class. - The child is called the derived class. - The relationship between the base class (parent) and its children is called an inheritance hierarchy. - Benefits of Inheritance a) Sharing - member functions and data - The parent defines the class members that are common to all its children Inheritance is a mechanism through which we can reuse and extend existing classes. The advantage is that we can extend code that already exists without the need to rewrite the original code (don't need to reinvent the wheel). b) Adding and Overriding - member functions and data - Each child *adds* or *overrides* what it inherits to implement its own unique behavior. The derived classes inherit the data members and the functions of the base class. The inherited functions could be overriden (modified) by the derived class. Moreover we can add new functions in the derived class. c) Manipulating different objects as a group - In a type-independent manner - We program using a common interface through a pointer to the base class. - The operation invoked is not determined until run-time - Based on the type of the object actually addressed. - B is base class - D is derived class: it possesses all the properties of B If get is a method of B D d; - d.get() can be called even though D does not define get(). - What is inherited and what is not: - Member functions and data members are inherited - Not inherited - these functions need information not available in base class - Constructors - Destructors - Assignment operator: argument is class specific - A D object is also a B object - A B object is not a D object - typing rule relaxation in one direction! - d = b is invalid - b = d is valid Syntax: class : access_type < name base class> { ... } The access type determines the way in which the base class members are inherited in the derived class. When access_type is public, all the public members of the base class become public members of the derived class. The private members of the base class become private in the derived class. This is relevant to the accessibility of members of the derived class by users of the derived class. The derived class can access public members of the base class. The derived class cannot access the private members of the base class. Example: class base_class { private: int x; public: void set_x (int n) { x = n;} void print_x () { cout << x << endl;} }; class derived_class : public base_class { private: int y; public: void set_y (int n) { y = n;} void print_y () { cout << y << endl;} }; int main() { derived_class obj; obj.set_x (10); // has access to the set_x function in the base class obj.set_y (20); obj.print_x(); // has access to the print_x function in the base class obj.print_y(); return 0; } However, the following is an error class derived_class : public base_class { private: int y; public: void set_y (int n) { y = n;} void set_x_y (int m, int n) { x = m; y = n;} //does not have direct //access to the private data //member x in the base class void print_y () { cout << y << endl;} }; how can we correct the above ? void set_x_y (int m, int n) { set_x(m); y = n;} //has access to the public //function member set_x //of the base class If access_type is private, all public (as well as private) members of the base class become private members in the derived class. Example: class derived_class : private base_class { private: int y; public: void set_y (int n) { y = n;} void print_y () { cout << y << endl;} }; int main() { derived_class obj; obj.set_x (10); // ERROR (set_x has become private in the derived class) obj.set_y (20); //OK obj.print_x(); // ERROR (print_x has become private in the derived class) obj.print_y(); //OK return 0; } Note that the set_x and print_x functions remain public in the base class (accessible to base class objects), no matter in what way (public or private) they are inherited by derived classes. Also, note that the derived class has access to the public functions in the base class - our previous example for using set_x inside the derived class is still correct in the case of private derivation. class derived_class : private base_class { private: int y; public: void set_y (int n) { y = n;} void set_x_y (int m, int n) { set_x(m); y = n;} //does not have direct //access to the private data //member x in the base class //but has access to the //function set_x which //becomes its own private function void print_y () { cout << y << endl;} }; Use of constructors and destructors with Inheritance The base class and the derived class could have constructor and/or destructor functions. When they both contain constructors and destructors, the constructors are called in the order of the derivation (first the constructor of the base class and then the constructor of the derived class). The destructors are executed in reverse order (the destructor of the derived class is executed before the destructor of the base class). class base_class { private: int x; public: base_class () { cout << "Base class constructor" << endl;} ~base_class () { cout << "Base class destructor" << endl;} void set_x (int n) { x = n;} void print_x () { cout << x << endl;} }; class derived_class : public base_class { private: int y; public: derived_class () { cout << "Derived class constructor" << endl;} ~derived_class () { cout << "Derived class destructor" << endl;} void set_y (int n) { y = n;} void print_y () { cout << y << endl;} }; int main() { derived_class obj; return 0; } The output is "Base class constructor" "Derived class constructor" "Derived class destructor" "Base class destructor" How about constructors with arguments ? If only the constructor of the derived class takes an argument, then we can just use what we already know. class derived_class : public base_class { private: int y; public: derived_class (int n) { y = n;}; etc. Nothing else changes. However we may need to pass an argument to the constructor of the base class (while constructing an object of the derived class). We need to pass to the constructor of the derived class all arguments necessary for both the derived AND the base class. Then the derived class passes the arguments necessary for the base class constructor. To allow for this the syntax changes as follows: derived class constructor (arg list1) : base class constructor (arg list2) { //body of derived class constructor } class base_class { private: int x; public: base_class (int n) { x = n;} }; class derived_class : public base_class { private: int y; public: derived_class (int n, int m) : base_class (n) {// passing the argument // to the base class y = m; }; main(){ derived_class obj (10, 20); obj.print_x(); obj.print_y(); } Protected members ----------------- A derived class cannot access the private members of the base class. There are situations when we want to keep a member private but we want it to be accessible by derived classes. protected members - accessible to any class derived from the class class { private: protected: public: }; Example of constructor ordering ------------------------------- #include using namespace std; class A { public: A(int i) : m_i(i) { cout << "A" << endl; } ~A() { cout << "~A" << endl; } int get() { return m_i; } private: int m_i; }; class B : public A { public: B(int i, int j) : A(i), m_j(j) { cout << "B" << endl; } ~B() { cout << "~B" << endl; } private: int m_j; }; int main() { A a(5); A *ap; /* prints A */ B b(2,3); /* prints A B */ a = b; /* B object can be assigned to an A object! */ ap = &b; /* pointer to B can be assigned to a pointer to A */ cout << b.get() << endl; /* shared function between A and B, prints 2 */ /* prints "~B ~A", "~A" */ return 0; } - A is base class - B is derived class: it possesses all the properties of A - b.get() can be called even though B does not define get(). Outputs 2. - An A object is not a B object - typing rule relaxation in one direction! - b = a is invalid - Object of Class B cannot access private data/function members of A, e.g. m_i. - Should use public member functions in A. - After ap = &b has been assigned, ap can't invoke any functions unique to B. Constructor Ordering ------------------------- - B constructor called on object b in main. - Passes integer values 2 and 3. - B constructor calls A constructor. - Passes value 2 to A constructor via initializer list. - A constructor initializes member m_i with passed value 2. - Body of A constructor runs and outputs "A". - B constructor initializes member m_j with passed value 3. - Body of B constructor runs and outputs "B". - Derived class constructor automatically calls base class default constructor - Unless some base class constructor is called explicitly - If base class has no default constructor, then derived class must call some base class constructor Destructor Ordering ------------------------ - B destructor called on object b in main - Body of B destructor runs and outputs "~B" - B destructor calls member m_j "destructor", int is a built-in type, so no-op - B destructor calls A destructor - Body of A destructor runs and outputs "~A" - A destructor calls member m_i destructor, again a no-op - Compare dtor and ctor order: - At the level of each class, order of steps is reversed in ctor vs. dtor - ctor: base class, members, body - dtor: body, members, base class