C++ Inheritance
- Summary
-
Discussion
- Could you explain C++ inheritance with an example?
- What are different types of inheritance in C++?
- How does C++ deal with the diamond problem or ambiguity?
- What's the concept of name hiding in C++?
- What is object slicing in C++?
- What are compile-time and runtime bindings in the context of inheritance?
- What are the rules of inheritance involving C++ abstract classes?
- What are the different visibility modes in inheritance in C++?
- What's the influence of access specifiers on virtual functions?
- How is inheritance of struct different from that of class?
- What are the workings of C++ class constructors and destructors?
- What are the main criticisms of C++ inheritance?
- Milestones
- Sample Code
- References
- Further Reading
- Article Stats
- Cite As
Inheritance is one of the most important principles of object-oriented programming. In C++, inheritance takes place between classes wherein one class acquires or inherits properties of another class. The newly defined class is known as derived class and the class from which it inherits is called the base class. Class inheritance reflects heredity in the natural world, where characteristics are transferred from parents to their children.
In C++, there are many types of inheritance namely, single, multiple, multilevel, hierarchical, and hybrid. C++ also supports different modes of inheritance. These are public, private, and protected.
Inheritance promotes code reuse. Reusing code not only makes code easy to understand but also reduces redundancy. Code maintenance is easier. Code reliability is improved.
Discussion
-
Could you explain C++ inheritance with an example? Consider two different classes,
Dog
andCat
. Both classes might have similar attributes such as breed and colour. In C++, such attributes are called data members. The classes might also have similar behaviours or actions such as eat, sleep and speak. In C++, such actions are implemented as member functions. Instead of writing similar code in two different classes we can create anAnimal
class that brings together common attributes and behaviours. ClassesDog
andCat
can inherit fromAnimal
class. They can add their own functionalities.In this example,
Animal
is the base class,Dog
andCat
are derived classes. We may say that (Animal, Dog) represents a parent-child relationship. Dogs are inheriting characteristics of animals. We may also say thatAnimal
is a generalization ofDog
andCat
; andDog
andCat
are specializations ofAnimal
.Derived classes can specialize by adding extra data members and member functions. They can also override the general behaviour defined in the base class.
Inheritance in C++ follows a bottom up approach. Derived classes acquire properties from base classes but the reverse is not true.
-
What are different types of inheritance in C++? C++ supports many types of inheritance based on the number of base or derived classes and the class hierarchy:
- Single Inheritance: A derived class inherits from only one base class. Eg.
class B
inherits fromclass A
. - Multiple Inheritance: A derived class inherits from more than one base class. Eg.
class C
inherits from bothclass A
andclass B
. - Multilevel Inheritance: The class hierarchy is deeper that one level. Eg.
class C
inherits fromclass B
that itself is inherited fromclass A
. - Multipath Inheritance: A derived class inherits from another derived class and also directly from the base class of the latter. Eg.
class C
inherits fromclass B
andclass A
whenclass B
itself is inherited fromclass A
.
While the above types are about a single class, there are others that are about the structure of the class hierarchy. These are more design patterns than types:
- Hierarchical Inheritance: A base class is specialized in many derived classes, which in turn are specialized into more derived classes.
- Hybrid Inheritance: This combines both multiple and hierarchical inheritance.
- Single Inheritance: A derived class inherits from only one base class. Eg.
-
How does C++ deal with the diamond problem or ambiguity? The diamond ambiguity can occur with multiple inheritance. It happens when the base classes themselves are derived from another common base class. The final derived class ends up with multiple copies of the distant base class.
Consider D inheriting from classes B1 and B2, B1 inheriting from A, and B2 inheriting from A. Thus, D has multiple copies of A. The problem occurs when a call is made to a member of A from an instance of D. C++ compiler can't determine which copy of A to use. Compilation will fail.
To solve this problem, C++ uses the concept of virtual inheritance. When inheriting A, B1 and B2 will specify the
virtual
keyword. The compiler sees this and creates a single instance or copy of A within D. Novirtual
keyword is needed when defining D.Having multiple copies of a base class is not really an error. In fact, it's possible to define a class that uses both virtual and non-virtual inheritance. Ultimately, the compiler should be able to determine member access unambiguously.
-
What's the concept of name hiding in C++? Name hiding happens when a derived class has a member with the same name as a member of the base class. The derived class definition hides the base class definition. Thus,
Animal::age
is hidden byDog::age
.The concept is also related to but different from overriding. For example,
Dog::speak()
if defined overridesAnimal::speak()
. In addition,Dog::speak()
can explicitly call the base class implementation with the statementAnimal::speak()
.C++ also allows functions to be overloaded, which is about having multiple member functions of the same name that differ in their parameter types. So, we could have
Animal::speak()
and theDog
class that defines onlyDog::speak(const string&)
. Unfortunately, in this case,Dog
class doesn't have access toAnimal::speak()
. In this case, we have name hiding, not overriding.It's possible for
Dog
class to have access toAnimal::speak()
by simply including the lineusing Animal::speak;
in its class definition. With thisusing
directive, all the overloadedspeak()
functions of the base class become accessible in the derived class. -
What is object slicing in C++? When a derived class object is typecast into one of its base classes, we're doing what's called object slicing. Conceptually, it's similar to typecasting a decimal number to an integer type, whereby the number loses it's decimal portion. With object slicing, the object loses members specialized in the derived class and retains only those parts of the base class to which it's been typecast.
Consider a base class A and a derived class B. Consider object b of type B. Object slicing happens with the assignments
A a = b
andA& a_ref = b
. Assignment operator is not virtual in C++. Hence, in these assignments the assignment operator of A is called and not that of B.The figure shows another example. Base class defines data member
a
and virtual functionsbar1()
andbar2()
. Derived class overridesbar1()
, and addsbar3()
,bar4()
andb
. An A-type slice of B can accessa
, derived class functionbar1()
and base class functionbar2()
. -
What are compile-time and runtime bindings in the context of inheritance? If the C++ compiler can determine what function to call at compile time, we call it compile-time binding, early binding or static dispatch. However, where virtual functions are defined in a base class and overridden by derived classes, the compiler can't know which function to call. This information is available only at runtime, giving rise to the terms runtime binding, late binding or dynamic dispatch.
Suppose classes
Square
andTriangle
are derived fromShape
. Consider member functionsvirtual void Shape::area() {...}
,void Square::area() {...}
andvoid Triangle::area() {...}
. Also defined is the functionpaintArea(Shape& shape) { int area = shape.area(); ... }
. Exactly which area function is called insidepaintArea()
? This is known only at runtime.Under the hood, runtime binding relies on vpointers and vtables. Every class with virtual functions has a vtable that stores pointers to virtual function definitions. When a virtual function is overridden, the pointer would point to the derived class implementation. Every class with a vtable also has a vpointer that points to the correct vtable.
-
What are the rules of inheritance involving C++ abstract classes? An abstract class in C++ has at least one pure virtual function, which essentially defines the interface but doesn't supply an implementation. Abstract classes are meant to be used as base classes for other class definitions. Abstract classes themselves can't be instantiated. Their very purpose is inheritance that conforms to an interface.
An abstract class can't be used as a parameter type, a function return type or the result of an explicit conversion. Pointers and references to an abstract class are allowed.
If a derived class inherits from an abstract class without supplying implementations, the derived class is also an abstract class. Thus, "abstraction" can be inherited. On the other hand, it's possible to inherit from a non-abstract base class, add pure virtual functions or override a non-pure virtual function with a pure virtual function, and thereby define an abstract derived class.
When a pure virtual function is called from a constructor, the behaviour is undefined.
-
What are the different visibility modes in inheritance in C++? C++ supports three access specifiers: public, protected and private. These access specifiers are used on data members and member functions. If not explicitly mentioned, private access is the default.
Likewise, a derived class can use an access specifier on each of its base classes. Available access specifiers are public, protected, and private. Where multiple inheritance is used, each base class can have a different access specifier. If not explicitly mentioned, private inheritance is the default.
Regardless of the access specifier on the base class, base class private members will remain private; that is, derived class can't access them. For public and protected members of base class, we summarize how access specifier on the base class affects access to members of the derived class:
- Public: Public and protected members of base class become public and protected members of derived class respectively.
- Protected: Public and protected members of base class become protected members of derived class.
- Private: Public and protected members of base class become private members of derived class.
-
What's the influence of access specifiers on virtual functions? The figure shows three examples involving the member function
A::name()
. In (a), the function is non-virtual. Hence, when it's called via the base class pointer,A::name()
is called. This is really compile-time binding.In (b), the function is made virtual. Now when the same call is made, runtime binding happens and
B::name()
is called. AlthoughB::name()
is private, it's called via the base class pointer andA::name()
itself is public.In (c), we make the inheritance protected. This makes
A::name()
protected in the derived class. This means that it can't be called from outside the class, such as frommain()
. Hence we get a compile-time error just as ifB::name()
had been declared protected or private. -
How is inheritance of struct
different from that ofclass
?Data structure
struct
comes from C programming language and is applicable in C++ as well. C++ extendsstruct
so that it can include member functions. Likeclass
, astruct
definition can also be inherited.The main difference is that when access specifiers aren't specified,
struct
members are public by default whereasclass
members are private by default. Likewise, when access specifiers are omitted in inheritance,struct
inheritance is public by default whereasclass
inheritance is private by default.For completeness, we note that there's also
union
that comes from C. It's members are public by default. Unions can't be used as base classes. -
What are the workings of C++ class constructors and destructors? Constructors and destructors were traditionally not inherited. Since C++11, constructors could be inherited.
Base class constructors are called before derived class constructors. In the case of multiple inheritance, base classes constructors are called in the depth-first left-to-right order of inheritance in the derived class. Destructors execute in reverse order.
Constructors have be defined as public or protected so that derived class constructors can call base class constructors.
Constructors need not be virtual: constructors are always called by name. Destructors have to be virtual. In other words, it's not sufficient to destroy only the base class object. We need to destroy the original derived class object.
-
What are the main criticisms of C++ inheritance? Inheritance in C++ is complex. Even a single inheritance has six variants: private/protected/public and virtual/non-virtual. With multiple inheritance, this complexity increases. The utility of a private virtual function is rather limited.
The designer of a base class decides if a member function must be declared virtual. Derived class can't control this or prevent calls to base class non-virtual functions. Virtual inheritance is also a problem since the decision is made early. It prevents defining a derived class that wants two copies of the distant base class.
C++ implementation of polymorphism via virtual functions can impact performance. Virtual member functions are not directly called. Instead, they've to be looked up via vpointer and vtable.
Milestones
1979
At Bell Laboratories, inspired by Simula, Bjarne Stroustrup conceives the idea of C with Classes. The new language would combine the low-level features of C and high-level code organization of Simula. Even in these early days, the language includes classes, class hierarchies and simple inheritance. Also included are private and public inheritance of a base class.
To support runtime polymorphism, virtual functions are added to the language. Only with the introduction of virtual functions, the language claims to support object-oriented programming. This is also when the first implementation becomes available to users. Subsequently, the language is renamed to C++ (1984) and the first commercial release happens (1985).
1989
2011
ISO publishes the C++11 standard, formally named ISO/IEC 14882:2011. This release introduces identifiers override
and final
. These help manage complex class hierarchies. They can be applied on virtual member functions when overridden in a derived class. Identifier final
can also be applied to a class so that it can't be inherited. Constructors can now be inherited with the using
declaration. This is useful when a derived object needs to be initialized in the same way as the base object.
Sample Code
References
- Alex. 2021. "17.5 — Inheritance and access specifiers." Tutorial, Learn C++, August 2. Accessed 2022-01-05.
- Arias, Pablo. 2017. "Understanding Virtual Tables in C++." Blog, June 10. Accessed 2022-01-05.
- Arora, Preeti, and Pinky Gupta. 2016. "Computer Science with C++." Sultan Chand & Sons.
- Boccara, Jonathan. 2020. "Virtual, final and override in C++." Blog, Fluent C++, February 21. Accessed 2022-01-07.
- Cain, Jerry. 2007. "Advanced Inheritance and Virtual Methods." Handout 07, CS107L: Programming Paradigms Laboratory, Stanford University, November 16. Accessed 2021-12-27.
- Chauhan, Shailendra. 2021. "Understanding Inheritance and Different Types of Inheritance." DotNetTricks, August 31. Accessed 2022-01-05.
- Cppreference. 2021a. "C++ 11." Cppreference, November 10. Accessed 2021-12-29.
- Cppreference. 2021b. "Using-declaration." Cppreference, September 15. Accessed 2022-01-05.
- Cppreference. 2021c. "Access specifiers." Cppreference, November 16. Accessed 2022-01-05.
- Cppreference. 2021d. "C++ 17." Cppreference, October 4. Accessed 2021-12-29.
- Fertig, Andreas. 2019. "Using base class constructor." Blog, January 9. Accessed 2022-01-07.
- Frankomania. 2008. "What is object slicing?" StackOverflow, November 8. Accessed 2021-12-27.
- IBM. 2021. "z/OS XL C/C++ Language Reference." v2.5, IBM Corporation, September 12. Updated 2021-09-30. Accessed 2022-01-05.
- ISO. 2011. "ISO/IEC 14882:2011 Information technology — Programming languages — C++." Edition 3, September. Accessed 2022-01-07.
- ISO. 2017. "ISO/IEC 14882:2017 Information technology — Programming languages — C++." Edition 5, December. Accessed 2022-01-07.
- Joyner, Ian. 1992. "C++??: A Critique of C++." 2nd Edition. Accessed 2022-01-07.
- Meredith, Alisdair, Michael Wong, and Jens Maurer. 2008. "Inheriting Constructors (revision 5)." N2540=08-0050, JTC1/SC22/WG21 - The C++ Standards Committee - ISOCPP, February 29. Accessed 2022-01-07.
- Milea, Andrei. 2021. "Solving the Diamond Problem with Virtual Inheritance." Tutorial, Cprogramming.com. Accessed 2021-12-27.
- Rieck, Bastian. 2016. "Surprises with name hiding in C++." Blog, January 31. Accessed 2022-01-06.
- Roy, Nirmalya. 2012. "Inheritance." Slides, CptS 122 – Data Structures, Washington State University, October 19. Accessed 2022-01-06.
- Sakkinen, Markku. 1992. "A Critique of the Inheritance Principles of C++." Computing Systems, vol. 5, no. 1, pp. 69-110, Winter. Accessed 2022-01-07.
- Segal, Josh. 2021. "C++ Inheritance Memory Model." Geek Culture, on Medium, May 28. Accessed 2022-01-05.
- Smolsky, Oleg. 2015. "Extension to aggregate initialization." P0017R1, JTC1/SC22/WG21 - The C++ Standards Committee - ISOCPP, October 24. Accessed 2022-01-07.
- Stroustrup, Bjarne. 1993."A history of C++: 1979–1991." HOPL-II: The second ACM SIGPLAN conference on History of programming languages, pp. 271-297, April. doi: 10.1145/154766.155375. Accessed 2022-01-07.
- Stroustrup, Bjarne. 2007. "Evolving a language in and for the real world: C++ 1991-2006." HOPL III: Proceedings of the third ACM SIGPLAN conference on History of programming languages, pp. 4-1–4-59, June. doi: 10.1145/1238844.1238848. Accessed 2022-01-07.
- Stroustrup, Bjarne. 2020. "Thriving in a crowded and changing world: C++ 2006–2020." Proceedings of the ACM on Programming Languages, vol. 4, Issue HOPL, Article No. 70, pp. 1-168, June. Accessed 2022-01-07.
- Stroustrup, Bjarne. 2021. "Bjarne Stroustrup's FAQ." July 23. Accessed 2022-01-07.
- Wikipedia. 2021a. "Inheritance (object-oriented programming)." Wikipedia, November 14. Accessed 2021-11-27.
Further Reading
- IBM. 2021. "z/OS XL C/C++ Language Reference." v2.5, IBM Corporation, September 12. Updated 2021-09-30. Accessed 2022-01-05.
- Gray, Jan. 1994. "C++: Under the Hood." Microsoft. Accessed 2021-12-30.
- Roy, Nirmalya. 2012. "Inheritance." Slides, CptS 122 – Data Structures, Washington State University, October 19. Accessed 2022-01-06.
- Udacity. 2021. "C++ Inheritance Explained." Blog, Udacity, June 29. Accessed 2022-01-05.