Here, we cover one of the object-orientated concepts of the C++ programming language, i.e., inheritance & related design concepts, which change with respect to the context and requirements. Before deep diving into the details, let’s take a moment and check the below snippet:
class Car;
class Vehicle
{
Vehicle()
{
std::cout << "Vehicle Cons" << std::endl;
}
friend class Car;
};
class Car: virtual Vehicle
{
public:
Car()
{
std::cout << "Car Cons" << std::endl;
}
};
class HotWheels : public Car
{
public:
HotWheels()
{
std::cout << "HotWheels Cons" << std::endl;
}
};
We will come back to this with an answer; till then, let’s check some other details.
Is-a relationship
Let’s start with the basic concept related to inheritance, i.e. public inheritance: everything that applies to the base class must also apply to derived classes.
class Vehicle{};
class Helicopter: public Vehicle{};
void start(const Vehicle& v);
void fly(const Helicopter& s);
//main
Vehicle vehicle;
Helicopter helicopter;
start(vehicle);
start(car); //OK
fly(vehicle); //NOK (Why?)
Why: A helicopter is a vehicle but not vice versa.
Is it always true that “everything that applies to the base class must also apply to derived classes”? Let’s check with the below snippet:
class Vehicle
{
public:
virtual void startEngine()
{
//default impl
}
};
//Let's consider normal human powered bicycle
class Bicyle: public Vehicle
{
// Bicycle can't start engine, that means everything that //applies to Vehicle class can't be applied to Bicycle class.
};
Alternative designs for this scenario: the base class has an impure virtual function, the derived class is inherited publicly, and that function doesn’t fit and needs alteration.
- Use a different hierarchy for the startEngine() method:
class Vehicle
{
//action
};
class VehicleWithEngine
{
public:
virtual void startEngine()
{
//default impl
}
};
class Bicyle: public Vehicle
{
//action
};
2. Use error message in derived class redefinition
class Vehicle
{
public:
virtual void startEngine()
{
//default impl
}
};
class Bicycle: public Vehicle
{
public:
void startEngine()
{
throw "Attempt to start engine in a bicycle."
}
};
This gives a runtime error if the client tries to access the startEngine method for the derived class object, i.e. Bicycle. The good interface prevents invalid code from compiling, not the runtime surprises.
Do you know which SOLID principle the above snippet represents?
Is-implemented-in-terms-of (aka Has-a)
Both private inheritance and composition mean is-implemented-in-terms-of. We will check one by one and each one’s advantage.
Private Inheritance: It should be used 1) when derived class needs to access protected members or need to redefine virtual function 2) Empty object optimization for library developers.
class Engine{};
class Vehicle: private Engine{};
void start(const Engine& e){}
//main
Vehicle vehicle;
start(vehicle); //Complains (Why?)
Why: This is not an “is-a” relationship anymore. In this context, an engine is not a vehicle, so when we try to use it, it complains.
Empty object Optimization:
class Empty{};
class Vehicle
{
int data;
Empty empty;
}; // sizeof(Vehicle) > sizeof(int)
class Vehicle1: private Empty
{
int data;
}; // sizeof(Vehicle1) == sizeof(int)
As both composition and private inheritance mean the same thing. When to use which one?
class Engine
{
protected:
void startEngine()
{
typeEngine();
}
virtual void typeEngine() = 0;
};
class Car: public Engine
{
public:
void start()
{
Base::StartEngine();
}
protected:
virtual void typeEngine()
{
std::cout<<"Fossil engine"<<std::endl;
}
};
Composition can be used when we want to prevent derived classes from redefining the base class (private) virtual function.
class Timer
{
public:
virtual void OnBuzz()
{
//Impl
}
};
class HornTimer: public Timer
{
public:
virtual void OnBuzz()
{
//impl
}
};
class Car
{
public:
HornTimer hornTimer;
};
The derived classes from class Car can’t redefine the virtual function. This kind of restriction is not possible for private inheritance.
Use composition whenever you can, use private inheritance whenever you must.
Hide & Seek
What is the output of the below snippet?
class Vehicle
{
public:
virtual void startEngine() = 0;
virtual void startEngine(int x);
virtual void startEngine(int x, int y);
int getNumber();
int getNumber(double);
};
class Car: public Vehicle
{
public:
void startEngine(){}
int getNumber(){}
};
//main
Car car;
car.startEngine(10); //NOK in compilation (Why1)
car.getNumber(10.0); //NOK in compilation (Why2)
Why1 & Why2: The overridden method in the derived class hides base class methods with the same name irrespective of virtual or non-virtual. So this complains at compile time.
Only Interface
Purpose of pure virtual function: The derived classes inherit this as a function interface only. This can have a definition, and the only way to call it would be to call it with the class name.
Of course, we can’t create an object of the class that has a pure virtual function, and the concrete class must redeclare this.
Interface + Mandatory Impl
Purpose of Base class Non-virtual function: The derived classes inherit this as a function interface as well as a mandatory implementation.
Do we need to redefine the non-virtual function in the derived class? What is the output of the below snippet?
class Vehicle
{
public:
void move()
{
std::cout<<"Vehicle can move"<<std::endl;
}
};
class Car: public Vehicle
{
public:
void move()
{
std::cout<<"Car can move"<<std::endl;
}
};
//main
Car car;
Vehicle *vehicle = &car;
vehicle->fly();// Vehicle can move (Why?)
Why: Non-virtual function is statistically bounded. So the pointer can’t detect the object type during runtime. This is not the desired output, so never redefine the non-virtual function in the derived class.
Interface + Default Impl
Purpose of impure virtual function: The derived class can fall back to the default implementation of the base class if it is not redefined in the derived class.
When can this feature of an impure virtual function lead to a problem?
//Honda Car Company Class
class Car
{
public:
virtual void startEngine()
{
//default implementation
}
};
//To avoid redundant code in ModelA and ModelB which belong to //same group, default implementation is provided in base class
class ModelA: public Car
{
};
class ModelB: public Car
{
};
//Another Car company Nissan merge with Honda, some car //models(i.e. ModelC) of Nissan come under Honda group.
class ModelC: public Car //Not Desirable (Why)
{
};
Why: This is not desirable as the ModelC class doesn’t want the default implementation of the base class, i.e. Honda. It has its own implementation.
Alternative design for this scenario: Use Pure Virtual Function
class Car
{
public:
virtual void startEngine() = 0; //act as Interface
};
void Car::startEngine()
{
//default impl
}
class ModelA: public Car
{
public:
void startEngine()
{
Car::startEngine(); //default impl
}
};
class ModelB: public Car
{
public:
void startEngine()
{
Car::startEngine(); //default impl
}
};
class ModelC: public Car
{
public:
void startEngine()
{
//Own impl
}
};
A pure virtual function is used only as an interface, while an impure virtual function can be used as both an interface and a default implementation, but it has it’s own caveats with respect to use cases as above.
How do we design a car class where the filling/recharge operation can vary depending upon car type while they have common pre- and post-operation?
For requirements like the above, derived classes decide how functionality (or operation) is implemented specific to their type, but the base class decides when the function is called. This is also known as the non-virtual interface idiom or template design pattern.
class Car
{
public:
void getFuel()
{
stop(); //pre-process
addFuel();
start(); //post-process
}
private:
virtual void start()
{
//default impl
}
virtual void stop()
{
//default impl
}
virtual void addFuel() = 0; //act as Interface
};
class ElectricCar: public Car
{
public:
void addFuel()
{
//Impl to connect charging station
}
};
class Fossil Car: public Car
{
public:
void addFuel()
{
//Impl to connect fossil station
}
};
Restrict Inheritance Property Of a Class
This sounds a bit odd in an article on inheritance, but let’s check it out.
class Car;
class Vehicle
{
Vehicle()
{
std::cout << "Vehicle Cons" << std::endl;
}
friend class Car;
};
class Car: virtual public Vehicle
{
public:
Car()
{
std::cout << "Car Cons" << std::endl;
}
};
class HotWheels : public Car
{
public:
HotWheels() //Complains (Why?)
{
std::cout << "HotWheels Cons" << std::endl;
}
};
Why: Constructors for virtual base classes anywhere in the class’s inheritance hierarchy are called by the “most derived” class’s constructor. And that constructor is private in the above snippet.
Multiple inheritance, which creates ambiguity for the access of the base class data member because of replication. And to overcome this diamond problem, we can use virtual inheritance, which doesn’t create replication. But it has its side effects, like slowing down access to data members in virtual base classes, complex initialisation, and larger object size.
Summary:
- In public inheritance, everything that applies to the base class must also apply to derived classes but not vice versa.
- Use composition whenever you can, but use private inheritance whenever you must.
- Pure virtual functions can have definitions, and the only way to call them would be to call them with the class name.
- Know the difference between 1) interface only, 2) interface with default impl 3) interface with mandatory impl.

- Reference: Effective C++ & https://isocpp.org/
Do you know what lvalue and rvalue references are? Check this link for details.
Leave a comment