Skip to main content

The four pillars of OOP

Object oriented programming

Encapulation

  • A class is built to encapsulate behaviour & state
  • It exposes members through a well thought-out design.
  • To enforce encapulation we make use of access specifiers
    • private: Only this class can access private members.
    • protected: Only this class and derived classes can access these members.
    • public: Members that are public are accessable outside the class.

Abstraction

  • A class should hide implementation while exposing only members that are required
  • To enforce this, we use abstract classes, interfaces.

Encapulation & Abstraction differences

These two concepts seem a bit too related so here are some differences

EncapulationAbstraction
Hides dataHides implementation
Focuses on securityFocuses on simplicity
Uses access modifiersUses abstract classes/interfaces

Inheritance

  • Common behaviour can be abstracted & encapsulate into a base class. Other classes can inherite properties & behaviour from a base class.
  • Inheritance promotes code reusability

Polymorphism

  • Polymorphism means many forms.
  • There are two types of polymorphism. Compile time & Runtime polymorphism.
    • Compile-time Polymorphism: Achieved via Method Overloading, Operator Overloading, Curiously Recurring Template Pattern (CRTP), Templates.
    • Runtime Polymorphism: Achieved via Function Overriding using the virtual keyword. This allows a base class pointer to call the specific implementation of a derived class.

Compile-type polymorphism (CRTP)

Curiously Recurring Template Pattern (CRTP) is a C++ technique where a class inherits from a template instantiated with itself. The derived class passes itself as the template parameter.

It is often used to emulate interfaces/mixins without virtual overhead.

template<typename Derived>
class Animal {
public:
void speak() {
static_cast<Derived*>(this)->speakImplementation();
}
};

class Dog : public Animal<Dog> {
public:
void speakImplementation() {
std::cout << "Bark" << std::endl;
}
};

Note: Important Limitation CRTP is not substitutable polymorphism. You cannot do:

Animal* a = new Dog(); // invalid

Runtime polymorphism

class Animal {
public:
virtual ~Animal() {}

virtual void speak() {
std::cout << "..." << std::endl;
}
};

class Dog : public Animal {
public:
void speak() override {
std::cout << "Bark" << std::endl;
}
};

Animal* animal = &baseAnimal; // speak() should output "..."
Dog dog; // speak() should output "Bark"

animal = &dog;
animal->speak(); // "Bark"

When you call animal->speak();, the program looks into the vtable (virtual table) for the Dog class and executes Dog::speak() Even though the typeof(animal) is Animal. This is runtime polymorphism.

CRTP vs Virtual Functions

CRTPVirtual Functions
Compile-time dispatchRuntime dispatch
No vtableUses vtable
No RTTI requiredRTTI often associated
Can inline easilyHarder to inline
FasterMore flexible
Types fixed at compile timeTypes chosen at runtime