Design Patterns in C++: Advanced Techniques and Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Advanced Design Patterns in C++: From Theory to Complex Implementations

\n\n

Design patterns are proven solutions to typical problems that arise during software development. While basic patterns (Singleton, Factory, Observer) are familiar to every developer, advanced techniques require a deep understanding of the C++ language, its templates, multiple inheritance, and metaprogramming. In this article, we will analyze four powerful patterns: Visitor, Strategy, State, and CRTP — with real code examples and explanations of when to use them.

\n\n

1. Visitor Pattern — Extending Functionality Without Changing Classes

\n\n

The Visitor pattern allows you to add new operations to a class hierarchy without modifying the classes themselves. This is especially useful when you have a stable object structure but frequently changing processing algorithms.

\n\n

Example: Processing Geometric Shapes

\n\n
#include <iostream>\n#include <vector>\n#include <memory>\n\n// Forward declaration\nclass Circle;\nclass Rectangle;\n\n// Abstract visitor\nclass Visitor {\npublic:\n    virtual void visit(Circle& circle) = 0;\n    virtual void visit(Rectangle& rectangle) = 0;\n    virtual ~Visitor() = default;\n};\n\n// Abstract element\nclass Shape {\npublic:\n    virtual void accept(Visitor& visitor) = 0;\n    virtual ~Shape() = default;\n};\n\n// Concrete elements\nclass Circle : public Shape {\npublic:\n    double radius;\n    explicit Circle(double r) : radius(r) {}\n    void accept(Visitor& visitor) override {\n        visitor.visit(*this);\n    }\n};\n\nclass Rectangle : public Shape {\npublic:\n    double width, height;\n    Rectangle(double w, double h) : width(w), height(h) {}\n    void accept(Visitor& visitor) override {\n        visitor.visit(*this);\n    }\n};\n\n// Concrete visitor — area calculation\nclass AreaVisitor : public Visitor {\npublic:\n    double totalArea = 0.0;\n    void visit(Circle& circle) override {\n        totalArea += 3.14159 * circle.radius * circle.radius;\n    }\n    void visit(Rectangle& rectangle) override {\n        totalArea += rectangle.width * rectangle.height;\n    }\n};\n\nint main() {\n    std::vector<std::unique_ptr<Shape>> shapes;\n    shapes.push_back(std::make_unique<Circle>(5.0));\n    shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));\n\n    AreaVisitor areaCalc;\n    for (auto& shape : shapes) {\n        shape->accept(areaCalc);\n    }\n    std::cout << "Total area: " << areaCalc.totalArea << std::endl;\n    return 0;\n}\n
\n\n

When to use: when you need to perform different operations on objects of a complex hierarchy without modifying their code. The drawback is that adding a new type requires changing all visitors.

\n\n

2. Strategy Pattern — Flexible Algorithm Switching on the Fly

\n\n

Strategy encapsulates a family of interchangeable algorithms and allows them to be swapped at runtime. In C++, this is often implemented via std::function or virtual classes.

\n\n

Example: Data Sorting System

\n\n
#include <iostream>\n#include <vector>\n#include <algorithm>\n#include <memory>\n#include <functional>\n\n// Context using a strategy\nclass Sorter {\nprivate:\n    std::function<void(std::vector<int>&)> strategy_;\npublic:\n    explicit Sorter(std::function<void(std::vector<int>&)> strategy)\n        : strategy_(std::move(strategy)) {}\n\n    void setStrategy(std::function<void(std::vector<int>&)> newStrategy) {\n        strategy_ = std::move(newStrategy);\n    }\n\n    void sort(std::vector<int>& data) {\n        if (strategy_) {\n            strategy_(data);\n        }\n    }\n};\n\n// Concrete strategies\nvoid bubbleSort(std::vector<int>& da                

Blogs

Book Recommendations