# C++ Class Hierarchy Design

## Introduction

In C++ library design and development, sometimes it is unclear what kind of good class hierarchy to design in the first place so that later as the library becomes more mature it does not have to take the risk of changing interface significantly.

In this blog post, I would like to discuss the best practice for class hierarchy design.

## C++ Class Hierarchy Design Guideline

The best practice for C++ class hierarchy design, according to Jon Kalb and Scott Meyers, is that “Classes should be used as bases or concrete (leaf) classes, not both.”

To make a class base, we make it an abstract class by providing it at least one pure virtual function (a derived class that does not override every pure virtual function is also an abstract class) and/or make its constructors protected.

To make a class leaf, we declare it as final so that it cannot be further derived and override each pure virtual function so that it can be instantiated.

In addition, it is very common to have base class pointers pointing to derived classes moving around. Trying to dereference a base class pointer and do copy or move assignment is extremely error-prone. We make the copy and move assignment operators protected for the base classes and public for the leaf classes.

## Examples

### Animals

We created the following dummy example for the guideline. In this example, we have four classes Animal, CatBase, Lion and Tiger. Animal is an abstract base class and it is not intended to be instantiated. CatBase, derived from Animal, is also supposed to be a base class. It is not an abstract class because we have override the pure virtual destructor. However, we still cannot instantiate a CatBase object because the constructors are protected. Lion and Tiger are supposed to be leaf classes and we declared them as final.

The application runs fine as expected.

### Animals with Clones

In some scenarios, some (proprietary) libraries will only provide the header for base abstracted classes. In terms of instantiating a concrete class object, the library will provide instantiation functions that returns a pointer to base abstracted classes. So everything is handled by base class pointers and it is impossible to know what kind of concrete class it belongs to.

Creating a concrete copy of a concrete class object is also impossible, unless the concrete class has a clone method.

The application runs fine as expected.

## Conclusions

Classes should be used as bases or concrete (leaf) classes, not both.

Lei Mao

02-07-2022

02-07-2022