L – Liskov Substitution Principle (LSP) | Master CPP Design Patterns 2026

0b63979cd9494aa401d1fce2d73bb002
On: April 23, 2025

Liskov Substitution Principle (LSP)

The Liskov Substitution Principle is one of the SOLID principles of object-oriented programming. It was introduced by Barbara Liskov in 1987 and it states:

“Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.”

What It Really Means:

Imagine you’re working with a class called Bird that has a method fly(). If you create a subclass Penguin from Bird, but Penguin can’t fly, then substituting Bird with Penguin could break your program. That’s a violation of LSP.

The principle pushes us to design subclasses that truly behave like their parent class, so code that uses the base class doesn’t have to worry about the specific subclass being used.

LSP in Action (Simple Example):

class Bird {
public:
    virtual void fly() {
        std::cout << "Flying..." << std::endl;
    }
};

class Sparrow : public Bird {
public:
    void fly() override {
        std::cout << "Sparrow flying!" << std::endl;
    }
};

// This class violates LSP
class Ostrich : public Bird {
public:
    void fly() override {
        throw std::logic_error("Ostriches can't fly!");
    }
};

In the example above, Ostrich breaks LSP because it can’t perform the fly() behavior expected from Bird.

Why It Matters:

  • Encourages safe inheritance.
  • Makes code more reliable and maintainable.
  • Helps avoid unexpected runtime errors.
  • Ensures that your class hierarchy makes logical sense.

How to Stick to LSP:

  • Don’t override methods in a way that changes their expected behavior.
  • Use composition over inheritance if subclass behavior doesn’t match the base class.
  • Clearly define contracts (what methods are supposed to do) and ensure subclasses respect them.

What is LSP?

Definition by Barbara Liskov:

If S is a subtype of T, then objects of type T may be replaced with objects of type S without altering the correctness of the program.

Simple Version:

A subclass should be able to stand in for its parent class without breaking the program.

What does that mean?

Let’s break it down:

  • You create a base class (Parent class).
  • Then you make a derived class (Child class).
  • The child should behave in a way that’s expected from the parent.
  • You should be able to use the child wherever you used the parent, and things should just work.

Real-Life Analogy

Let’s say:

  • You have a class “Bird” with a method fly().
  • You create a subclass “Parrot” — fine, it can fly.
  • Now you create a subclass “Penguin” — 🐧 uh-oh! Penguins can’t fly.

If you call fly() on a Penguin object assuming it’s a Bird, your program breaks the expectation.

🚫 That violates LSP!

Example in C++ that Violates LSP

class Bird {
public:
    virtual void fly() {
        cout << "Flying..." << endl;
    }
};

class Sparrow : public Bird {
public:
    void fly() override {
        cout << "Sparrow flying" << endl;
    }
};

class Ostrich : public Bird {
public:
    void fly() override {
        throw runtime_error("Ostriches can't fly!");
    }
};

Problem:

  • If someone uses Bird* b = new Ostrich(); b->fly(); expecting it to fly — it crashes.
  • Ostrich is a Bird, but it doesn’t behave like a Bird should.

This violates LSP.

LSP-Compliant Design

Solution: Refactor your classes to respect behavior expectations.

class Bird {
public:
    virtual void eat() {
        cout << "Bird eating" << endl;
    }
};

class FlyingBird : public Bird {
public:
    virtual void fly() = 0;
};

class Sparrow : public FlyingBird {
public:
    void fly() override {
        cout << "Sparrow flying" << endl;
    }
};

class Ostrich : public Bird {
    // No fly() here
};

Now:

  • Ostrich doesn’t pretend to be a flying bird.
  • If your logic requires only flying birds, you use the FlyingBird type.
  • LSP is safely respected.

LSP in Code: A Simple, Clear Example

class Rectangle {
public:
    virtual void setWidth(int w) { width = w; }
    virtual void setHeight(int h) { height = h; }
    virtual int getArea() { return width * height; }

protected:
    int width;
    int height;
};

Now we make a Square from Rectangle:

class Square : public Rectangle {
public:
    void setWidth(int w) override {
        width = height = w;
    }

    void setHeight(int h) override {
        width = height = h;
    }
};

Problem:

If someone does this:

Rectangle* r = new Square();
r->setWidth(5);
r->setHeight(10);
cout << r->getArea();  // Expected: 50, But it gives 100

Uh-oh! Because setting width or height sets both in Square.
This breaks expected behavior.
It violates LSP.

LSP-Compliant Refactor

Instead, don’t inherit Square from Rectangle directly if they behave differently.

Maybe use composition or separate hierarchy.

LSP Summary Table

ConceptBad (Violates LSP)Good (Follows LSP)
Penguin inherits Bird with fly()Penguins can’t flySeparate class for non-flying birds
Square inherits RectangleArea breaks expectationsDesign Square separately
Subclass breaks parent’s ruleThrows, skips behaviorBehaves as parent promises

Key Tips to Follow LSP

  • Derived classes must honor contracts/behavior of base class.
  • Don’t override methods in a way that changes expected behavior.
  • Use interfaces or abstract classes to separate capabilities (like flying).
  • Prefer composition over inheritance when behaviors differ.

Want to Try a LSP-based Mini Project?

We can build:

  • A PaymentMethod base class (pay())
  • Subclasses: CreditCard, UPI, Wallet
  • And see how breaking or respecting LSP affects real logic

FAQ on Liskov Substitution Principle (LSP) in C++

1. What is the Liskov Substitution Principle (LSP) in C++?

The Liskov Substitution Principle (LSP) is one of the SOLID principles of object-oriented programming. It states that objects of a derived class should be replaceable with objects of their base class without affecting the correctness of the program. In simple terms, subclasses should behave like their parent classes without breaking functionality.

2. Why is the Liskov Substitution Principle important in software design?

LSP ensures code reliability, reusability, and maintainability. By following it, developers prevent unexpected behaviors when using inheritance. It reduces bugs, improves abstraction, and makes the system easier to extend in the future.

3. Can you give a simple C++ example of the Liskov Substitution Principle?

Yes.

#include 
using namespace std;

class Bird {
public:
    virtual void fly() { cout << "Bird can fly\n"; }
};

class Sparrow : public Bird {
public:
    void fly() override { cout << "Sparrow flying high\n"; }
};

// Substitution works correctly
int main() {
    Bird* b = new Sparrow();
    b->fly();  // Works fine as Sparrow behaves like Bird
    delete b;
    return 0;
}

Here, Sparrow can substitute Bird without breaking the program, which follows LSP.

4. What happens if LSP is violated in C++?

Violating LSP leads to runtime errors, unexpected behaviors, and poor code design. For example, if a derived class overrides a base class function in a way that changes its expected behavior, the system may produce wrong results or even crash.

5. How does the Liskov Substitution Principle relate to C++ design patterns?

Many C++ design patterns, such as Strategy, Template Method, and Factory Method, rely on LSP to ensure that subclasses can replace base classes seamlessly. Without LSP, these patterns lose their flexibility and correctness.

6. How can I check if my C++ code follows the Liskov Substitution Principle?

You can verify by asking:

  • Can I replace every instance of the base class with the subclass?
  • Does the subclass maintain the expected behavior of the parent?
    If the answer is yes, then your code follows LSP.

7. What are real-life examples of the Liskov Substitution Principle?

  • Vehicles: A Car is a type of Vehicle and can be used wherever a Vehicle is expected.
  • Shapes: A Circle should behave like a Shape without altering the expected behavior of a Shape class.
  • Payment Systems: A CreditCardPayment should work wherever a generic PaymentMethod is required.

8. How does LSP improve C++ code quality?

  • Promotes polymorphism
  • Reduces tight coupling
  • Makes code testable and scalable
  • Encourages clean architecture with fewer bugs

9. What is the difference between LSP and the Open-Closed Principle (OCP)?

  • LSP ensures that subclasses can replace base classes without breaking functionality.
  • OCP ensures that classes are open for extension but closed for modification.
    Both are connected — violating LSP often means violating OCP as well.

10. How is Liskov Substitution Principle used in modern C++ projects in 2025?

In 2025 C++ development, LSP is applied in:

  • Embedded systems for safe hardware abstraction
  • Game development for character and object hierarchies
  • Enterprise applications for scalable and maintainable architectures
  • Design patterns to ensure flexible and reusable solutions

You can also Visit other tutorials of Embedded Prep

Special thanks to @mr-raj for contributing to this article on Embedded Prep

Leave a Comment