Einleitung: Warum der Streit zwischen OOP und funktionalem Ansatz nicht abebbt?
\n\nWenn Sie jemals darüber nachgedacht haben, welchen Programmierstil in C++ Sie für ein neues Projekt wählen sollen, sind Sie nicht allein. Der Streit zwischen funktionaler Programmierung und objektorientiertem Ansatz in C++ dauert bereits seit Jahrzehnten an. Auf der einen Seite die klassische OOP, die an Universitäten gelehrt wird. Auf der anderen Seite die modernen funktionalen Techniken, die die Standards C++11, C++14, C++17 und C++20 mit sich brachten.
\n\nAuf der Website pythonlib.ru diskutieren wir oft Architekturentscheidungen, und heute ist es an der Zeit, diese Frage ein für alle Mal zu klären. Welcher Stil ist schneller? Welcher ist einfacher zu warten? Und vor allem – kann man sie kombinieren?
\n\nIn diesem Artikel werden wir nicht fanatisch eine Paradigma verteidigen. Stattdessen analysieren wir reale Codebeispiele, zeigen die Vor- und Nachteile jedes Ansatzes und geben praktische Empfehlungen. Los geht's!
\n\nKurzer Überblick über die Paradigmen in C++
\n\nBevor wir tiefer eintauchen, frischen wir kurz auf, worum es eigentlich geht.
\n\nObjektorientierte Programmierung (OOP)
\n\nOOP in C++ basiert auf drei Säulen: Kapselung, Vererbung und Polymorphismus. Sie erstellen Klassen, die Daten und Methoden zu deren Verarbeitung zusammenfassen. Dies ist der klassische Ansatz, der in den 90er und 2000er Jahren dominierte.
\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\nFunktionale Programmierung (FP)
\n\nDer funktionale Ansatz in C++ ist nicht reines Haskell, sondern eher die Nutzung von FP-Ideen im Rahmen einer imperativen Sprache. Schlüsselelemente sind: Lambda-Funktionen, Unveränderlichkeit von Daten, Funktionen höherer Ordnung und Komposition.
\n\nstd::vector<int> numbers = {1, 2, 3, 4, 5};\n\n// Funktionaler Stil: Filtern und Transformieren\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 << " "; // Ausgabe: 4 16\n}\n\n\nPraktischer Vergleich: Drei typische Aufgaben
\n\nNehmen wir drei reale Aufgaben, mit denen ein C++-Entwickler konfrontiert wird, und sehen wir, wie OOP und FP sie lösen.
\n\nAufgabe 1: Verarbeitung einer Datensammlung
\n\nBedingung: Es gibt eine Liste von Bestellungen. Teure Bestellungen (Preis > 1000) sollen gefiltert, nach Datum sortiert und die Top 5 ausgegeben werden.
\n\nOOP-Stil:
\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\nFunktionaler Stil:
\n\nauto getTopExpensiveRecent(const std::vector<Order>& orders, int count) {\n return orders\n | std::views::filter([](const Order& o) { return o.price >