Skip to content

Understanding Inheritance

Inheritance is a fundamental component of any object oriented programming language such as C++.

Access Modifiers

There are three access modifiers used in C++ classes: public, private, and protected.

  • public: Members that are public are accessible everywhere inside the program.
  • private: Members that are privated are accessible only within the class which they were defined.
  • protected: Members that are protected are accessible in the class where they were defined and any class that inherits from that class.

Note that if private or protected members of a class need to be accessed elsewhere in the program, this can be done by exposing those members through public methods. Getters and setters are an example of this.

Polymorphism

If you're creating a variable that takes a template parameter, then the class that you pass to the variable can also be any inherited class. This is a common pattern in most programming languages and enables what is known at the strategy pattern

For example

cpp
std::unordered_map<std::string, Parent> map;

class Child : public Parent {
	//…
}

Child c = Child();

// A Child can be inserted into the map
// since it inherits from Parent
map.insert(std::pair<std::string, Child>("a", c));
std::unordered_map<std::string, Parent> map;

class Child : public Parent {
	//…
}

Child c = Child();

// A Child can be inserted into the map
// since it inherits from Parent
map.insert(std::pair<std::string, Child>("a", c));

Virtual Inheritance

Virtual inheritance enforces that only one copy of a base class's members will be inherited in a grandchild class (second-level derivative). Typically, virtual inheritance is the desired behaviour, so it's a good idea to use it by default. To use virtual inheritance when creating a derived class, prepend virtual to the parent class.

cpp
struct Animal {
    virtual ~Animal() = default;
    virtual void Eat() {}
};

// Two classes virtually inherit from Animal
struct Mammal : virtual Animal {
    virtual void Breathe() {}
};

struct WingedAnimal : virtual Animal {
    virtual void Flap() {}
};

// Now the Animal representation is the same for both 
// Mammal and WingedAnimal, so Bat::Eat is unambiguous
struct Bat : Mammal, WingedAnimal {};
struct Animal {
    virtual ~Animal() = default;
    virtual void Eat() {}
};

// Two classes virtually inherit from Animal
struct Mammal : virtual Animal {
    virtual void Breathe() {}
};

struct WingedAnimal : virtual Animal {
    virtual void Flap() {}
};

// Now the Animal representation is the same for both 
// Mammal and WingedAnimal, so Bat::Eat is unambiguous
struct Bat : Mammal, WingedAnimal {};

The above example is pulled directly from Wikipedia - Virtual Inheritance.

Abstract Classes

An abstract class is a class that is designed to be specifically used as a base class. An abstract class contains at least one pure virtual function. You declare a pure virtual function by using a pure specifier (= 0) in the declaration of a virtual member function in the class declaration. -- IBM - Abstract Classes

cpp
// Abstract base class with pure virtual methods
class ObjectInterface {
public:
  virtual ~ObjectInterface(){};
  virtual int poll(std::string &msg) const noexcept = 0;
  virtual int publish(const std::string &msg) const = 0;
  virtual int subscribe(const std::string &topic) const = 0;
  virtual int handshake(const uint16_t system_id) = 0;
private:
  virtual void initialize(const std::string &endpoint) = 0;
};

// Child 
class Object : virtual public ObjectInterface {
  explicit Object(const uint16_t port, const std::string &ip) noexcept :
    _port(port), _ip(ip) {};
  virtual ~AccessNode();

  int poll(std::string &msg) const noexcept override { /*…*/ }
  int publish(const std::string &msg) const override { /*…*/ }
  int subscribe(const std::string &topic) const override { /*…*/ }
  int handshake(const uint16_t system_id) override { /*…*/ }
  void initialize(const std::string &endpoint) override { /*…*/ }

  std::string _ip;
  uint16_t    _port;

  // Delete copy and assignment copy constructors
  AccessNode(const AccessNode &)            = delete;
  AccessNode &operator=(const AccessNode &) = delete;
};
// Abstract base class with pure virtual methods
class ObjectInterface {
public:
  virtual ~ObjectInterface(){};
  virtual int poll(std::string &msg) const noexcept = 0;
  virtual int publish(const std::string &msg) const = 0;
  virtual int subscribe(const std::string &topic) const = 0;
  virtual int handshake(const uint16_t system_id) = 0;
private:
  virtual void initialize(const std::string &endpoint) = 0;
};

// Child 
class Object : virtual public ObjectInterface {
  explicit Object(const uint16_t port, const std::string &ip) noexcept :
    _port(port), _ip(ip) {};
  virtual ~AccessNode();

  int poll(std::string &msg) const noexcept override { /*…*/ }
  int publish(const std::string &msg) const override { /*…*/ }
  int subscribe(const std::string &topic) const override { /*…*/ }
  int handshake(const uint16_t system_id) override { /*…*/ }
  void initialize(const std::string &endpoint) override { /*…*/ }

  std::string _ip;
  uint16_t    _port;

  // Delete copy and assignment copy constructors
  AccessNode(const AccessNode &)            = delete;
  AccessNode &operator=(const AccessNode &) = delete;
};

ADDITIONAL RESOURCES