Proper Use of Polymorphism in C++ Demian Kellermann
June 6, 2011
Polymorphism in C++ Introduction
I
Everyone here knows how to write class hierarchys
I
But how many of you have ever used private inheritance?
I
And what exactly does public inheritance mean for a design?
Proposal Even experienced programmers tend to overuse (public) inheritance Why? In the following talk I will outline some of the pitfalls of using inheritance and how to design good class hierarchys.
2 / 31
Polymorphism in C++ Introduction
Outline 1 Introduction 2 Interface or Implementation 3 Meaning of Inheritance
Public Inheritance: Is-A Private Inheritance: Is-Implemented-In-Terms-Of Containment vs Inheritance 4 Inheritance vs Templates 5 Uses and Misuses of Multiple Inheritance
There are multiple use cases for class inheritance hierarchies: I
To specify and implement a common interface
I
To extrapolate common functionality in a base class
I
Provide common functionality and default behavior
How are those use cases implemented?
4 / 31
Polymorphism in C++ Interface or Implementation
Interface definitions Example: Define an interface to interact with a database class DatabaseInterface { public : v i r t u a l bool c o n n e c t ( C o n n e c t i o n I n f o &i n f o ) = 0 ; v i r t u a l DataItem q u e r y ( DataQuery &q u e r y ) = 0 ; };
Note I
Instead of a default implementation, the member functions are pure virtual
I
Classes that only define pure virtual member functions are called Protocol Classes
I
Inheriting classes must implement their own version of the member functions 5 / 31
Polymorphism in C++ Interface or Implementation
Normal Base Classes Classes that are intended as base classes may provide mandatory and optional default implementations of member functions. c l a s s Car { // o p t i o n a l d e f a u l t i m p l e m e n t a t i o n v i r t u a l s t r i n g honk ( ) { r e t u r n ” Hoooonk ” ; } // mandatory d e f a u l t i m p l e m e n t a t i o n unsigned i n t g e t S e r i a l N u m b e r ( ) { return serialNum ; } } 6 / 31
Polymorphism in C++ Interface or Implementation
To recapitulate I
Inheriting classes must implement pure virtual member functions of the base class
I
They may override virtual member functions of the base class
I
They should not override non-virtual member functions
I
Although it is not forbidden...
7 / 31
Overriding non-virtual member functions Note Never override non-virtual member functions in derived classes c l a s s Base { void foo ( ) { . . } } c l a s s D e r i v e d : p u b l i c Base { void foo ( ) { . . . } }
D e r i v e d ∗d = new D e r i v e d ( ) ; d−>f o o ( ) // D e r i v e d : : f o o ( ) Base ∗ b = d ; b−>f o o ( ) // Base : : f o o ( )
Polymorphism in C++ Meaning of Inheritance
1 Introduction 2 Interface or Implementation 3 Meaning of Inheritance
Public Inheritance: Is-A Private Inheritance: Is-Implemented-In-Terms-Of Containment vs Inheritance 4 Inheritance vs Templates 5 Uses and Misuses of Multiple Inheritance
Polymorphism in C++ Meaning of Inheritance Public Inheritance: Is-A
Public Inheritance To refresh your memory I
The subclass may access all public and protected members of the base class
I
Public and protected members of the base class become public and protected members of the inheriting class
I
Calling member functions of the base class on a derived object implicitly calls the base class member function
Note Public inheritance always represents an ”IS-A” relationship ⇒ That means any action applicable to the base class must also be applicable to the derived class. 10 / 31
Polymorphism in C++ Meaning of Inheritance Public Inheritance: Is-A
class Rectangle { private : int w; int h ; public : void setWidth ( i n t w) ; void s e t H e i g h t ( i n t h ) ; };
c l a s s Square : public Rectangle { private : int s ; public : void s e t S i d e s ( i n t s ) ; };
Square s ; s . s e t W i d t h ( 4 2 ) ; // ? !
Note Although mathematically correct, a square IS NOT a rectangle when it comes to inheritance 11 / 31
Polymorphism in C++ Meaning of Inheritance Private Inheritance: Is-Implemented-In-Terms-Of
Private Inheritance Behavior I
The subclass may access all public and protected members of the base class
I
Public and protected members of the base class become private members of the inheriting class
I
Calling member functions of the base class on a derived object results in a compiler error.
Note Private inheritance represents a IS-IMPLEMENTED-IN-TERMS-OF relationship 12 / 31
Polymorphism in C++ Meaning of Inheritance Private Inheritance: Is-Implemented-In-Terms-Of
class Rectangle { private : int w; int h ;
c l a s s Square : private Rectangle { public : void s e t S i d e s ( i n t s ) { setWidth ( s ) ; setHeight ( s ); }
public : void setWidth ( i n t w) ; void s e t H e i g h t ( i n t h ) ; };
};
Square s ; s . s e t W i d t h ( 4 2 ) ; // C o m p i l e e r r o r s . s e t S i z e ( 2 3 ) ; // w o r k s a s e x p e c t e d
Note The square is now implemented in terms of a rectangle 13 / 31
Polymorphism in C++ Meaning of Inheritance Containment vs Inheritance
Containment vs Inheritance
t e m p l a t e c l a s s Queue : p r i v a t e l i s t {
t e m p l a t e c l a s s Queue { private : l i s t l i s t ;
public : v o i d e n q u e u e (T &i t e m ) { push back ( item ) } T& d e q u e u e ( ) { return pop front ( ) ; } }
public : v o i d e n q u e u e (T &i t e m ) { l i s t . push back ( item ) } T& d e q u e u e ( ) { return l i s t . pop front (); } }
The Question Which technique should be preferred?
14 / 31
Polymorphism in C++ Meaning of Inheritance Containment vs Inheritance
Proposal The preferred method to solve IS-IMPLEMENTED-IN-TERMS-OF relations should be Containment Why? Modelling simple HAS-A relationships with private inheritance leads to.. I
unneccessary compile-time dependencies
I
access to protected members of the base class (violates ”need to know” principle)
15 / 31
Polymorphism in C++ Meaning of Inheritance Containment vs Inheritance
When to use which? Use private inheritance when you.. I
need exactly one instance of the base class
I
need access to protected members of the base class
I
need to implement a (pure) virtual member function of the base class
I
want to influence the construction of a virtual base class
I
benefit greatly from emtpy base class optimization
Use Containment when you.. I
need more than one instance of the class you are containing
I
need to change the used instance at runtime
I
do not need to override or implement virtual functions
16 / 31
Polymorphism in C++ Inheritance vs Templates
1 Introduction 2 Interface or Implementation 3 Meaning of Inheritance
Public Inheritance: Is-A Private Inheritance: Is-Implemented-In-Terms-Of Containment vs Inheritance 4 Inheritance vs Templates 5 Uses and Misuses of Multiple Inheritance
Specialization One might think that inheritance and the template mechanism are two ways to solve similar problems: Create classes dependant on certain types. A look at two examples given by Scott Meyers:
Create different classes to manage stacks of certain objects. Each stack may only contain one type of object.
Develop a set of classes representing different races of cats. All cats eat and sleep, but they do it differently.
18 / 31
Polymorphism in C++ Inheritance vs Templates
How do we implement those classes?
Let’s try to use templates I
You can write a stack class without knowing anything about the stored objects
I
You can therefore implement the class with a template parameter T, and return objects of type T
I
You can not write a templated ”Cat”-class, because their behavior depends on the actual type
19 / 31
Polymorphism in C++ Inheritance vs Templates
How do we implement those classes?
OK then, let’s try inheritance I
You can write different stack classes for different types
I
But you cannot write an abstract base class for them: public virtual ??? pop() = 0;
I
Because the cats need different behavior, we implement a protocol class
I
We can then publicly inherit this base class in the classes for different breeds
20 / 31
Polymorphism in C++ Inheritance vs Templates
Looking at the problems While the problems seem similar at a first glance, the main difference lies in the relationship between the classes and their behavior. I
Whenever the behavior of your classes depends on actual types, use inheritance
I
When the actual types do not matter, use templates
21 / 31
Polymorphism in C++ Uses and Misuses of Multiple Inheritance
1 Introduction 2 Interface or Implementation 3 Meaning of Inheritance
Public Inheritance: Is-A Private Inheritance: Is-Implemented-In-Terms-Of Containment vs Inheritance 4 Inheritance vs Templates 5 Uses and Misuses of Multiple Inheritance
Polymorphism in C++ Uses and Misuses of Multiple Inheritance
Pandora’s Box? Multiple inheritance, while opening up the modelling process to a more natural approach, introduces problems not existant without it. I
What problems can arise?
I
Is it still worth it?
I
When should you use multiple inheritance?
23 / 31
Polymorphism in C++ Uses and Misuses of Multiple Inheritance Name clashes
Name clashes
Name ambiguity can be resolved by explicitly naming the class. But this makes virtual member functions non-virtual!
class A { public : void foo ( ) ; }; class B { public : void foo ( ) ; }; c l a s s AB : p u b l i c A , p u b l i c B {};
AB ab ; ab . f o o ( ) ; // C o m p i l e e r r o r ab : : A . f o o ( ) ; //A : : f o o ( ) ab : : B . f o o ( ) ; //B : : f o o ( )
24 / 31
Polymorphism in C++ Uses and Misuses of Multiple Inheritance Siamese Twin Functions
Siamese Twin Functions Example: You want to implement a graphical lottery simulation. You need to implement a Lottery-interface and an interface for graphical output:
class Lottery { v i r t u a l i n t draw ( ) = 0 ; }
c l a s s Window { v i r t u a l i n t draw ( ) = 0 ; }
The Problem We need to implement both functions differently, but they have the same signature. 25 / 31
Polymorphism in C++ Uses and Misuses of Multiple Inheritance Siamese Twin Functions
The solution We need to rename the ambiguous functions c l a s s LotteryDraw : public L o t t e r y { virtual int lotteryDraw () = 0; i n t draw ( ) { // o v e r r i d e return lotteryDraw ( ) ; } } c l a s s WindowDraw : p u b l i c Window { v i r t u a l i n t windowDraw ( ) = 0 ; i n t draw ( ) { // o v e r r i d e r e t u r n windowDraw ( ) ; } } 26 / 31
Polymorphism in C++ Uses and Misuses of Multiple Inheritance Siamese Twin Functions
c l a s s MyLottery : public LotteryDraw , p u b l i c WindowDraw { i n t windowDraw ( ) { . . } int lotteryDraw () {..} }; MyLottery l ; Window ∗ lw = & l ; L o t t e r y ∗ l l = &l ; lw−>draw ( ) ; // windowDraw ( ) l l −>draw ( ) ; // l o t t e r y D r a w ( )
27 / 31
Polymorphism in C++ Uses and Misuses of Multiple Inheritance Discussion
Discussion Should you use multiple inheritance? Are there benefits or would it be better to exclude it from the next C++ Standard?
Multiple inheritance can be a useful tool, if you know its limitations and problems I
Combine multiple library classes into a single object
I
Implement multiple protocol classes
I
Make full use of polymorphism
28 / 31
Polymorphism in C++ Conclusions
1 Introduction 2 Interface or Implementation 3 Meaning of Inheritance
Public Inheritance: Is-A Private Inheritance: Is-Implemented-In-Terms-Of Containment vs Inheritance 4 Inheritance vs Templates 5 Uses and Misuses of Multiple Inheritance