Einleitung: Warum sauberer Code für C++ wichtig ist
\n\nC++ ist eine leistungsstarke und flexible Sprache, die es ermöglicht, sowohl hoch effizienten Systemcode als auch komplexe Anwendungslösungen zu schreiben. Diese Flexibilität führt jedoch oft zu Chaos: Überladene Funktionen, implizite Konvertierungen, manuelle Speicherverwaltung und Makros können ein Projekt innerhalb weniger Monate in einen „Sumpf“ verwandeln. Das Konzept des Clean Code, popularisiert von Robert C. Martin, ist auf C++ vollständig anwendbar, jedoch unter Berücksichtigung der sprachspezifischen Besonderheiten.
\n\nIn diesem Artikel werden wir die wichtigsten Prinzipien von sauberem Code in C++ erläutern und ihre Anwendung anhand realer Beispiele zeigen. Sie erfahren, wie Sie Code schreiben, der leicht zu lesen, zu testen und zu warten ist.
\n\n1. Benennung und Struktur: Grundlage der Lesbarkeit
\n\n1.1. Aussagekräftige Namen für Variablen und Funktionen
\n\nDie erste Regel von sauberem Code ist, dass der Name die Absicht offenbaren sollte. In C++ ist dies aufgrund von Überladungen und Templates besonders wichtig.
\n\nSchlechtes Beispiel:
\n\nint d; // vergangene Zeit in Tagen\nvoid proc(int a, int b); // was sind a und b?\n\nGutes Beispiel:
\n\nint elapsedTimeInDays;\nvoid processData(const std::vector<int>& inputData, int maxIterations);\n\n1.2. Klassen und Namensräume
\n\nVerwenden Sie Namensräume zur logischen Gruppierung. Vermeiden Sie globale Variablen – das ist ein Anti-Pattern.
\n\nnamespace network {\n class TcpConnection {\n public:\n void connect(const std::string& host, int port);\n void send(const std::vector<char>& data);\n private:\n int socket_;\n };\n} // namespace network\n\n2. Funktionen: Klein und mit einer einzigen Verantwortung
\n\n2.1. Prinzip der einzigen Verantwortung (SRP)
\n\nEine Funktion sollte nur eine Sache tun. Wenn eine Funktion mehr als eine Aktion ausführt, teilen Sie sie auf.
\n\nSchlechtes Beispiel:
\n\nvoid processOrder(Order& order) {\n // Validierung\n if (order.total < 0) throw std::invalid_argument("Negativer Gesamtbetrag");\n // Speichern in der DB\n database.save(order);\n // E-Mail senden\n emailService.send(order.customerEmail, "Bestellung bestätigt");\n}\n\nGutes Beispiel:
\n\nbool validateOrder(const Order& order);\nvoid saveOrderToDatabase(const Order& order);\nvoid sendConfirmationEmail(const Order& order);\n\nvoid processOrder(Order& order) {\n if (!validateOrder(order)) return;\n saveOrderToDatabase(order);\n sendConfirmationEmail(order);\n}\n\n2.2. Vermeiden Sie Seiteneffekte
\n\nEine Funktion sollte den globalen Zustand nicht ohne explizite Notwendigkeit ändern. Verwenden Sie konstante Referenzen und geben Sie Werte zurück, anstatt Argumente zu ändern.
\n\n// Schlecht: Ändert den Eingabevektor\nvoid addPrefix(std::vector<std::string>& words, const std::string& prefix) {\n for (auto& word : words) {\n word = prefix + word;\n }\n}\n\n// Gut: Gibt einen neuen Vektor zurück\nstd::vector<std::string> addPrefix(const std::vector<std::string>& words, const std::string& prefix) {\n std::vector<std::string> result;\n result.reserve(words.size());\n for (const auto& word : words) {\n result.push_back(prefix + word);\n }\n return result;\n}\n\n3. Speicherverwaltung und RAII
\n\n3.1. Verwenden Sie intelligente Zeiger
\n\nIm modernen C++ (C++11 und neuer) gilt die manuelle Speicherverwaltung mit new/delete als veraltet. Verwenden Sie std::unique_ptr und std::shared_ptr.
// Schlecht: Speicherleck möglich\nvoid process() {\n Resource* res = new Resource();\n // ... wenn hier eine Ausnahme auftritt, tritt ein Speicherleck auf\n delete res;\n}\n\n// Gut: Automatische Freigabe\nvoid process() {\n a