[ C++ ]
I had previously written a blog post on how to write a plugin in ROS using pluginlib, and there I created an abstract class for structuring controllers for the robot. While that was a very good use case for ROS plugins, it was too specific for robotics, and I wanted to single out the concepts of abstract classes and make it clear how to write/think about abstract classes. So in this tutorial we will talk about how to think of different types of coffee maker as an abstract class.
- What is an abstract class?
- How do I write such a class?
- What is the difference between private, public, and protected inheritance?
What is an abstract class?
An abstract class is a class where you don’t have the implementations of the functions, but you have an idea of what it should do. You know what the inputs should be to the class, and what it should return, but how it handles those inputs and generates the outputs is not necessarily defined by the abstract class itself. To the outside world, the abstract class is kind of like black box, you give it some input, its derived classes or implementations all handle the inputs in some way, and then provides you with the output that you expect.
A class that implements an abstract class is called a derived class. The abstract class it derives from is called the parent class or base class.
An abstract class will have virtual functions. Virtual functions are functions in a class that you expect to be re-defined and implemented in the derived classes. Therefore, you can provide an implementation to a virtual function in an abstract class, then expect the derived class to re-define it later on if need be. If not, the base class implementation is used.
virtual bool virtualFunction()
{
//implementation (which can be overwritten by derived classes) here
}
Pure virtual functions are functions that are NOT given an implementation in the base class, so they must be implemented by the derived class. It is indicated by an = 0
at the end of your function declaration in the function declaration.
virtual bool pureVirtualFunction() = 0;
How do I write such a class?
It is very simple. We shall demonstrate with a really inane example with different types of coffee makers! We’ve all seen enough tutorials with shapes…
Think of it like this: a “coffee maker” adds water to coffee beans and produces coffee. How it handles the coffee beans, water, and how the process of adding water to coffee is different depending on the method.

Writing the abstract base class
The base class will just be called CoffeeMaker
. All types of the coffee maker will have attributes and functions described in this class. This is where we try to describe the general function and attributes of all of the children classes that will derive from this.
For example, for coffee makers, it usually suffices to include two functions:
prepareSetup()
makeCoffee()
The input is usually:
- coffee beans, which we will assume is of some type
beans_t
(obviously, this doesn’t exist, but we’ll assume this is a real type definition and is defined somewhere) - water, which is of type
water_t
and finally, the output is going to be:
- coffee, which is of type
coffee_t
.
Altogether now, our class definition should look something like:
coffee_makers/include/CoffeeMaker.h
#pragma once
namespace cafe {
class CoffeeMaker {
public:
CoffeeMaker() {};
virtual ~CoffeeMaker() {}; // Notice how the destructor is declared virtual
virtual bool prepareSetup() = 0;
virtual coffee_t makeCoffee(beans_t& beans, water_t& water)
{
groundbeans_t ground = grindBeans(beans, 18);
coffee_t coffee = pourWaterOverBeans(ground_beans, water);
};
protected:
groundbeans_t ground = grindBeans(beans_t& beans, double fineness);
coffee_t coffee = pourWaterOverBeans(groundbeans_t& ground, water_t& water);
};
} //namespace cafe
Let’s discuss a few things:
- Notice that the destructor for the
CoffeeMaker
class is declaredvirtual
. - Here,
prepareSetup()
is a pure virtual function, since every coffee making setup is different and requires a different set of procedures. - However,
makeCoffee()
is a virtual function. It has a basic implementation inside it, which is to literally just grind beans and add water to beans. - We’ve added two protected functions in the abstract class, which are not defined in the header (but should be defined in a
.cpp
file). These aregrindBeans()
andpourWaterOverBeans()
.
By the looks of it so far, the base class is looking like some sort of stale cold brew, since we haven’t specified any properties. Let’s mae some derived classes to implement some specifics.
Writing the derived class
First up, the classic pour-over. A basic pour-over goes something like this:
- Put pour-over dripper over a cup/carafe, either directly or on a stand
- Put filter inside dripper
- Heat water to some specified temperature
- Grind coffee beans to a medium-fine grind
- Put ground coffee into filter
- Pour hot water slowly over coffee beans
Here’s a relaxing tutorial from the guys at Hario!
coffee_makers/include/coffee_makers/PourOver.h
#pragma once
#include <coffee_makers/CoffeeMaker.h>
namespace cafe {
class PourOver : public CoffeeMaker {
public:
PourOver() {};
~PourOver() {};
bool prepareSetup();
coffee_t makeCoffee(beans_t& beans, water_t& water);
private:
// Here, you can declare things like.... filter, pour over cup, etc.
};
} //namespace cafe
Notice all the virtual declarations are gone! Now all we have to do is write the implementations of prepareSetup()
and makeCoffee()
, in a .cpp file, and include this header:
coffee_makers/src/PourOver.cpp
#include <coffee_makers/PourOver.h>
namespace cafe {
bool PourOver::prepareSetup()
{
// insert setup code here
}
coffee_t PourOver::makeCoffee(beans_t& beans, water_t& water)
{
// insert how you prefer to make your pourover here
}
} //namespace cafe
What is the difference between private, public, and protected inheritance?
For a base class like:
class Base {
public:
int publicMemberInBase;
protected:
int protectedMemberInBase;
private:
int privateMemberInBase;
};
In general
- Everything that is aware of
Base
is also aware thatBase
containspublicMemberInBase
. - Only the children (and their children) are aware that
Base
containsprotectedMemberInBase
. - No one but
Base
is aware ofprivateMemberInBase
.
By “is aware of”, I mean “acknowledge the existence of, and thus be able to access”.
Inheritance
Say, a child Child
inherits from Base
. Then if:
class Child : public Base
:- everything that is aware of
Base
andChild
is also awareChild
inherits fromBase
. - Therefore, a pointer to
Base
can be pointed to an instance ofChild
.
- everything that is aware of
class Child : protected Base
:- only
Child
and its children are aware that they inherit fromBase
. - Therefore, a pointer to
Base
cannot be pointed to an instance ofChild
.
- only
class Child : private Base
:- no one other than
Child
is aware of the inheritance. - Therefore, a pointer to
Base
cannot be pointed to an instance ofChild
.
- no one other than
Access inside inherited classes
class Child : public Base
:publicMemberInBase
is public to everyoneprotectedMemberInBase
is protected, therefore only accessible byChild
and children ofChild
privateMemberInBase
is not accessible fromChild
, or anyone except forBase
class Child : protected Base
publicMemberInBase
is protected, therefore only accessible byChild
and children ofChild
protectedMemberInBase
is protected, therefore only accessible byChild
and children ofChild
privateMemberInBase
is not accessible fromChild
, or anyone except forBase
class Child : private Base
publicMemberInBase
is private, therefore can only be accessed byChild
protectedMemberInBase
is private, therefore can only be accessed byChild
privateMemberInBase
is not accessible fromChild
, or anyone except forBase