Introduction: Why Doesn't the OOP vs Functional Approach Debate Die Down?
\n\nIf you've ever wondered which programming style in C++ to choose for a new project, you are not alone. The debate between functional programming and the object-oriented approach in C++ has been going on for decades. On one side — classic OOP, which was taught in universities. On the other — modern functional techniques brought by the C++11, C++14, C++17, and C++20 standards.
\n\nOn the website pythonlib.ru, we often discuss architectural decisions, and today it's time to settle this question once and for all. Which style is faster? Which is easier to maintain? And most importantly — can they be combined?
\n\nIn this article, we won't fanatically defend one paradigm. Instead, we'll analyze real code examples, show the pros and cons of each approach, and give practical recommendations. Let's go!
\n\nBrief Overview of Paradigms in C++
\n\nBefore diving in, let's quickly refresh what we're talking about.
\n\nObject-Oriented Programming (OOP)
\n\nOOP in C++ is built on three pillars: encapsulation, inheritance, and polymorphism. You create classes that combine data and methods for processing it. This is the classic approach that dominated the 90s and 2000s.
\n\nclass Animal {\npublic:\n virtual void speak() const = 0;\n virtual ~Animal() = default;\n};\n\nclass Dog : public Animal {\npublic:\n void speak() const override {\n std::cout << "Woof!" << std::endl;\n }\n};\n\nclass Cat : public Animal {\npublic:\n void speak() const override {\n std::cout << "Meow!" << std::endl;\n }\n};\n\nvoid makeSound(const Animal& animal) {\n animal.speak();\n}\n\n\nFunctional Programming (FP)
\n\nThe functional approach in C++ is not pure Haskell, but rather using FP ideas within an imperative language. Key elements: lambda functions, data immutability, higher-order functions, and composition.
\n\nstd::vector<int> numbers = {1, 2, 3, 4, 5};\n\n// Functional style: filtering and transformation\nauto result = numbers\n | std::views::filter([](int n) { return n % 2 == 0; })\n | std::views::transform([](int n) { return n * n; });\n\nfor (int n : result) {\n std::cout << n << " "; // Output: 4 16\n}\n\n\nPractical Comparison: Three Typical Tasks
\n\nLet's take three real tasks a C++ developer faces and see how OOP and FP solve them.
\n\nTask 1: Processing a Data Collection
\n\nCondition: There is a list of orders. We need to filter expensive ones (price > 1000), sort by date, and output the top 5.
\n\nOOP style:
\n\nclass OrderProcessor {\npublic:\n void addOrder(Order order) { orders_.push_back(std::move(order)); }\n \n std::vector<Order> getTopExpensiveRecent(int count) const {\n auto filtered = orders_;\n filtered.erase(\n std::remove_if(filtered.begin(), filtered.end(),\n [](const Order& o) { return o.price <= 1000; }),\n filtered.end());\n \n std::sort(filtered.begin(), filtered.end(),\n [](const Order& a, const Order& b) {\n return a.date > b.date;\n });\n \n if (filtered.size() > count) {\n filtered.resize(count);\n }\n return filtered;\n }\n \nprivate:\n std::vector<Order> orders_;\n};\n\n\nFunctional style:
\n\nauto getTopExpensiveRecent(const std::vector<Order>& orders, int count) {\n return orders\n | std::views::filter([](const Order& o) { return o.price >