BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

C++ (Multiple) Inheritance

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:






Inheritance


Member Access



class_inheritance_diagram
  1. All members are inherited to the derived class.
  2. But the private member of the base class is not directly accessible.
  3. The inherited private member of a base class can be accessed via inherited public member function. In other words, it has to work through the base-class method.
  4. Public member of a base class inherited as a public member.


    1. The code below shows only public members are accessible from outside of the class:

      class A
      {
      public:
      	int xPublic;
      protected:
      	int xProtected;	
      private:
      	int xPrivate;
      };
      
      class B : public A
      {};
      
      int main(int argc, char** argv)
      {
      	A a;
      	a.xPublic = 0;
      	a.xProtected = 0; // error: inaccessible
      	a.xPrivate = 0;   // error: inaccessible
      
      	B b;
      	b.xPublic = 0;
      	b.xProtected = 0;  // error: inaccessible
      	b.xPrivate = 0;    // error: inaccessible
      
       	return 0;       
      }
      


      The following code shows that the inherited member is accessible within the class. But still even the inherited member accessing outside a class is not allowed (b.xProtected) because the same rule applies to the inherited member: protected member cannot be accessed from outside of a class (inherited member of a parent class remains as a protected member of a child class).

      class A
      {
      public:
      	int xPublic;
      protected:
      	int xProtected;	
      private:
      	int xPrivate;
      };
      
      class B : public A
      {
      public:
      	void foo(A *a, B *b)
      	{
      		a->xProtected = 0.0;     // error: A::xProtected inaccessible
      		b->xProtected = 0.0;     // OK: inherited member
      		this->xProtected = 0.0;
      	}
      }
      
      int main(int argc, char** argv)
      {
      	A a;
      	B b;
      	b.xProtected = 0.0;	// error: inaccessible outside of a class - inherited xProtected 
      	b.foo(&a;, &b;);
      
      	return 0;
      }
      

      (Note) Non-static protected members of a base class can not be accessed via a pointer or reference to the base class.



      The accessibility property of the introduced foo() method from the A type is private in the C type, hence it will not be publicly accessible. This is the case regardless of the fact that the A type is publicly derived and the foo method has public accessibility in A type. The alias created by the using declaration has the usual accessibility for a member declaration.

      #include <iostream>
      
      class A
      {
      public:
      	void foo() const { std::cout << "A"; }
      };
      
      class B
      {
      public:
      	void foo() const { std::cout << "B"; }
      };
      
      class C : public A, public B
      {
      	using A::foo;  // note that this is private section 
      };
      
      int main()
      {
      	B b;
      	b.foo();  // OK: B::foo()
      
      	C c;
      	c.foo();   // error: A::foo() or private member in class 'C'
      
      	return 0;
      }
      


      Constructing derived/base class

      A derived class does not have direct access to the inherited private member of a base class. A derived class can access to it via inherited public member function. In other words, it has to work through the base-class method.

      This applies to the constructor as well. When we construct a derived class object, the base object must be created first. If we do not specify any base-constructor, it calls a base-constructor. This is because the base-constructor does initialization of derived object's the inherited base-class member. The members of derived object are initialized by the derived-constructor.

      Remember, a derived-class constructor always calls a base-class constructor.

      #include <iostream>
      using namespace std;
      
      class A
      {
      public:
      	A(int n = 1) : ia(n) { cout << "A()" << endl; }
      
      protected:
          int ia;
      };
      
      class B : public A
      {
      public:
      	B(int n) : ib(n) { cout << "B()" << endl; }
      
      private:
          int ib;
      };
      
      int main()
      {
          B b(2);
      
          return 0;
      }
      

      Output:

      A()
      B()
      

      As we see from the result, the derived-class constructor calls the base-class constructor. Actually, the base-class object should be constructed before the code enters the body of the derived-class constructor.

      If we need to initialize inherited the base-class member with different value form a default value, we can use base-class constructor in the initializer list of the derived-class constructor. Let's say, if we want to set the inherited member as 12 not the default value of 1. Then, we use the following line of code for the initializer.

      #include <iostream>
      using namespace std;
      
      class A
      {
      public:
      	A(int n = 1) : ia(n) 
      	{ cout << "A() ia = " << ia <<  endl; }
      
      protected:
          int ia;
      };
      
      class B : public A
      {
      public:
      	B(int n) : ib(n), A(n+10)
      	{ cout << "B()" << endl; }
      
      private:
          int ib;
      };
      
      int main()
      {
          B b(2);
      
          return 0;
      }
      

      Output:

      A() ia = 12
      B()
      


      More on hierarchical construction/destruction of base and derived class

      In the following code, we have two classes A and B. The B is a derived class from the class A. In the constructor of B, we do not specify the A-class constructor to use. In the class B, we have two objects of type A as it's member. Here is the code:

      #include <iostream>
      using namespace std;
      
      class A
      {
      public:
      	A(int n = 1) : ia(n) 
      	{ cout << "A() ia = " << ia <<  endl; }
      	~A() 
      	{ cout << "~A() ia = " << ia << endl; }
      
      protected:
              int ia;
      };
      
      class B : public A
      {
      public:
      	B(int n) : a1(ia+10), a2(n)
      	{ cout << "B()" << endl; }
      	~B() 
      	{ cout << "~B() ia = " << ia-- << endl; }
      
      private:
      	A a2;
      	A a1;
      };
      
      int main()
      {
      	{B b(20);}
      
              return 0;
      }
      

      Output:

      A() ia = 1
      A() ia = 20
      A() ia = 11
      B()
      ~B() ia = 1
      ~A() ia = 11
      ~A() ia = 20
      ~A() ia = 0
      

      Let's review the output.
      First, the class B's constructor called the base-class A()'s default constructor which initialized ia as 1. Then we initialized the two A objects: a2 with n(=20) and a1 with ai+10(=1+10). Note that member initialization was done in the order of the appearance in the class definition but not in the order of the initializer list.

      When object b was out of {} block scope, the destructor of B called. As we see in the output, the value of the inherited member ia is 1 because it was constructed using default value in the A constructor. Then, the two A object members of B object called two destructors with ia values of 11 and 20. Finally, the base-object destructor called with ia = 0 since it was decremented by the B's destructor.



      The following code is very similar to the previous example except the class B has a member of pointer to an array of type A object.

      #include <iostream>
      using namespace std;
      
      class A
      {
      public:
      	A(int n = 1) : ia(n) 
      	{ cout << "A() ia = " << ia <<  endl; }
      	~A() 
      	{ cout << "~A() ia = " << ia << endl; }
      
      protected:
              int ia;
      };
      
      class B : public A
      {
      public:
      	B(int n) : a1(new A[5]), a2(n)
      	{ cout << "B()" << endl; }
      	~B() 
      	{ 
      		cout << "~B() ia = " << ia-- << endl;
      		delete[] a1;
      	}
      
      private:
      	A a2;
      	A* a1;
      };
      
      int main()
      {
             {B b(20);}
      
             return 0;
      }
      

      Output:

      A() ia = 1
      A() ia = 20
      A() ia = 1
      A() ia = 1
      A() ia = 1
      A() ia = 1
      A() ia = 1
      B()
      ~B() ia = 1
      ~A() ia = 1
      ~A() ia = 1
      ~A() ia = 1
      ~A() ia = 1
      ~A() ia = 1
      ~A() ia = 20
      ~A() ia = 0
      

      As we can see from the output, we call default constructor of A five more times because of the array A[5] in the initializer of B.



      How to block subclassing

      In Java, there is a feature called final class which prohibits users from inheriting derived class from the final class. But not in C++.

      However, we can achieve it in two ways:

      1. Make the constructor of the base class private.
        class A
        {
        public:
        	static A* create() {return new A();}
        private:
        	A() {};
        };
        
        class B : public A {};
        
        B b;   // Error :  cannot access to 
               //          the parent's private constructor A()
        
        This works. However, user should always call A::create() static function, and the object cannot be created on the stack. So, if we do not want that happening, we may opt to use the 2nd way as described below.
      2. Use virtual inheritance.
        class B;
        
        class A
        {
        private:
        	A() {};
        	friend class B;
        };
        
        class B : virtual public A {};
        
        class Derived : public B {};
                  // Error :  cannot access to  A()
        
        It's utilizing the following requirement:
        A derived class (Derived class in the example) with an indirect virtual base class (A in the example) should have its constructors (Derived::Derived()) invoke the indirect base class constructors (A::A()) directly, which is illegal for indirect nontrivial base classes.
        Normally, constructor A() would have been called from the constructor of the class B if there had been no virtual inheritance. It can be checked with the following code:
        #include <iostream>
        using namespace std;
        
        class B;
        
        class A
        {
        private:
        	A() {cout << "A()" << endl;};
        	friend class B;
        };
        
        // B can access A() because of the "friend"
        class B : public A 
        {public: B(){ cout << "B()" << endl;}};
        
        class Derived : public B 
        {public: Derived() {cout << "Derived()";}};
        
        int main()
        {
        	Derived dd;
        	return 0;
        }
        
        The output from the code:
        A()
        B()
        Derived()
          
        In other words, because of the virtual, the indirect (with respect to the for the Derived class) constructor A() is called from Derived::Derived() not from B::B(). But it's not accessible from Derived class due to the private access.



      Multiple Inheritance


      Order of construction

      Constructing an object of derived type involves constructing and initializing all the base subobjects. The base class constructors are invoked in the order in which they appear in the class derivation list. Given the class hierarchy in the code below, let's guess in what order the base constructors are constructed:

      #include <iostream>
      using namespace std;
      
      class X 
      { 
      public: 
      	X(){cout << "X() ";}
      	~X(){cout << "~X() ";}
      };
      
      class Y 
      { 
      public: 
      	Y(){cout << "Y() ";}
      	~Y(){cout << "~Y() ";}
      };
      
      class Z : public X, public Y 
      { 
      public: 
      	Z(){cout << "Z() ";}
      	~Z(){cout << "~Z() ";}
      };
      
      class A 
      { 
      public: 
      	A(){cout << "A() ";}
      	~A(){cout << "~A() ";}
      };
      
      class B : public A 
      { 
      public: 
      	B(){cout << "B() ";}
      	~B(){cout << "~B() ";}
      };
      
      class C : public B 
      { 
      public: 
      	C(){cout << "C() ";}
      	~C(){cout << "~C() ";}
      };
      
      class F : public C, public Z 
      { 
      public: 
      	F(){cout << "F() ";}
      	~F(){cout << "~F() ";}
      };
      
      
      int main()
      {
      	{F f;}
      
      	return 0;
      }
      

      Output:

      A() B() C() X() Y() Z() F() ~F() ~Z() ~Y() ~X() ~C() ~B() ~A()
      


      Order of destruction

      As we see from the output, the order of destruction is the reverse of the constructor calls.



      Virtual Base Classes

      Virtual base classes allow an object derived from multiple bases that themselves share a common base to inherit just one object of the shared base class.

      class Car {};
      class NormalCar : virtual public Car {};
      class ElectricCar : public virtual Car {};
      class HybridCar : public NormalCar, public ElectricCar {};
      

      Now a HybridCar object will contain a single copy of a Car object. Actually, the inherited NormalCar and ElectricCar objects share a common Car object instead of each bringing in its own copy so that HybridCar object now contains one Car object.



      Constructor Rules

      Virtual base classes force us to take a new approach to class constructors. In normal cases where we do not have virtual base classes, the only constructor that can appear in an initialization list are the constructors for immediate base classes. These constructors can, in turn, pass information onto their bases as shown in the example below:

      #include <iostream>
      using namespace std;
      
      class X
      {
      public:
      	X(int nx = 0):x(nx) {}
      	int x;
      };
      
      class Y : public X 
      {
      public:
      	Y(int ny = 0, int nx = 0): X(nx), y(ny){}
      	int y;
      };
      
      class	Z : public Y 
      {
      public:
      	Z(int nz = 0, int ny = 0, int nx = 0): Y(ny, nx), z(nz){}
      	int z;
      };
      
      int main()
      {
      	Z zObj(1, 2, 3);
      	cout << zObj.x << zObj.y << zObj.z << endl; //321
      
      	return 0;
      }
      

      A Z constructor can invoke only constructors from the Y, and a Y constructor can invoke only constructors from the X class. In the code, the Z constructor uses nz value and then passes the values of ny and nx back to the Y constructor. The Y constructor, then, uses the value of ny, and passes nx back to the X constructor. However, this automatic passing of information does not work if the Car is a virtual base class. So, the following code won't work, and may get similar error to the following complain from the compiler:

      'Car::Car' : no appropriate default constructor available
      

      Here is the problematic code regarding the constructor invoke when we have virtual base class:

      #include <iostream>
      #include <string>
      using namespace std;
      
      class Car 
      {
      public:
      	Car(string s):name(s){}
      	string name;
      };
      
      class NormalCar : virtual public Car 
      {
      public:
      	NormalCar(int d, string s=""): Car(s), displacement(d){}
      	int displacement;
      };
      
      class ElectricCar : public virtual Car 
      {
      public:
      	ElectricCar(int bl, string s = ""): Car(s), batteryLife(bl){}
      	int batteryLife;
      };
      
      class HybridCar : public NormalCar, public ElectricCar 
      {
      	
      public:
      	HybridCar(int p = 50000, int b = 1, int d = 2000, string s="") 
      		: NormalCar(d, s), ElectricCar(b, s), price(p) {}
      	int price;
      };
      
      int main()
      {
      	HybridCar h(65000, 2, 2500, "mcar");
      
      	return 0;
      }
      

      The problem is that automatic passing of information would pass s to the Car object via two separate paths (NormalCar and ElectricCar), and it's ambiguous. So, C++ disables the automatic passing through an intermedia class to a base class if the base class is virtual. Therefore, we need to use Car constructor directly in the initializer of the HybridCar as shown in the example below:

      #include 
      #include 
      using namespace std;
      
      class Car 
      {
      public:
      	Car(string s):name(s){}
      	string name;
      };
      
      class NormalCar : virtual public Car 
      {
      public:
      	NormalCar(int d, string s=""): Car(s), displacement(d){}
      	int displacement;
      };
      
      class ElectricCar : public virtual Car 
      {
      public:
      	ElectricCar(int bl, string s = ""): Car(s), batteryLife(bl){}
      	int batteryLife;
      };
      
      class HybridCar : public NormalCar, public ElectricCar 
      {
      	
      public:
      	HybridCar(int p = 50000, int b = 1, int d = 2000, string s="") 
      		: NormalCar(d, s), ElectricCar(b, s), Car(s), price(p) {}
      	int price;
      };
      
      int main()
      {
      	HybridCar h(65000, 2, 2500, "mcar");
      	cout << "name: " << h.name << endl;
      	cout << "displacement: " << h.displacement << endl;
      	cout << "batterLife: " << h.batteryLife << endl;
      	cout << "price: " << h.price << endl;
      
      	return 0;
      }
      

      Output:

      name: mcar
      displacement: 2500
      batterLife: 2
      price: 65000
      







Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master