Refactoring von Legacy-Code in C++: Fortgeschrittene Methoden und Strategien

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum Legacy-Code in C++ eine Herausforderung für Profis darstellt



Die Arbeit mit vererbtem Code in C++ ist nicht nur eine Aufgabe, sondern eine Kunst. Legacy-Projekte, die vor 10–15 Jahren geschrieben wurden, enthalten oft Raw Pointer, lange Funktionen, Präprozessor-Makros und fehlende Modultests. Alles von Grund auf neu zu schreiben, ist jedoch ein Luxus, den sich Unternehmen selten leisten können. Fortgeschrittenes Refactoring ermöglicht es, den Code schrittweise zu modernisieren, technische Schulden zu reduzieren und die Leistung zu steigern, ohne die Entwicklung zu unterbrechen.



In diesem Artikel betrachten wir fortgeschrittene Refactoring-Techniken für erfahrene C++-Entwickler: von der Beseitigung von Makros bis zur Einführung von RAII und Smart Pointern sowie Strategien zur sicheren Änderung der Architektur.



1. Analyse und Kartierung von Abhängigkeiten



Vor jedem Refactoring muss verstanden werden, wie der aktuelle Code aufgebaut ist. Verwenden Sie dazu statische Analyse (z. B. Clang Static Analyzer oder Cppcheck) und Tools zur Visualisierung von Abhängigkeiten (Doxygen + Graphviz).



1.1. Erstellung eines Aufrufgraphen



Erstellen Sie einen Aufrufgraphen für die Schlüsselmodule. Dies hilft, Folgendes zu identifizieren:

  • Zyklische Abhängigkeiten zwischen Modulen;
  • „Gottobjekte“ (God classes), die zu viel wissen;
  • Toten Code, der nie aufgerufen wird.


// Beispiel: Verwendung von Doxygen zur Generierung eines Graphen// In den Funktionskommentaren geben wir Abhängigkeiten an/** * @brief Verarbeitet eine Benutzeranfrage * @param request - Anfragestruktur * @return true, wenn die Verarbeitung erfolgreich ist * @see UserManager, RequestValidator */bool ProcessRequest(const Request& request);


1.2. Erstellung eines Test-Harness



Ohne Tests ist Refactoring wie ein Gang durch ein Minenfeld. Beginnen Sie mit dem Schreiben von Charakterisierungstests (characterization tests), die das aktuelle Verhalten des Codes festhalten. Verwenden Sie Google Test oder Catch2 für C++.



// Beispiel eines Charakterisierungstests für eine alte FunktionTEST(LegacyModuleTest, CalculateDiscount_ReturnsCorrectValue) {    // Arrange    LegacyCalculator calc;    // Act    double result = calc.CalculateDiscount(100.0, "VIP");    // Assert — aktuelles Verhalten festhalten, auch wenn es unlogisch erscheint    ASSERT_DOUBLE_EQ(result, 85.0);}


2. Ersetzen von Makros durch constexpr- und Inline-Funktionen



Makros (#define) sind der größte Feind der Lesbarkeit und Typsicherheit in Legacy-C++. Sie unterliegen nicht den Gültigkeitsbereichsregeln und können schwer zu findende Fehler verursachen.



2.1. Schrittweise Ersetzung



Anstatt alle Makros auf einmal zu entfernen, verwenden Sie die „Strangler-Fig“-Strategie:

  1. Finden Sie das Makro und identifizieren Sie alle Stellen, an denen es verwendet wird.
  2. Ersetzen Sie es durch eine constexpr-Konstante oder eine inline-Funktion.
  3. Kompilieren Sie neu und führen Sie die Tests aus.
  4. Entfernen Sie das ursprüngliche #define.


// War (Legacy):#define MAX_BUFFER_SIZE 4096#define SQUARE(x) ((x) * (x))

// Wurde zu (modernes C++):constexpr size_t kMaxBufferSize = 4096;template<typename T>constexpr T Square(T x) noexcept { return x * x;}


Wichtig: Für Makros, die sich wie Funktionen verhalten (mit Argumenten), verwenden Sie variadische Templates oder std::invoke, wenn das Makro __VA_ARGS__ verwendet hat.



3. Übergang von Raw Pointern zu Smart Pointern



Raw Pointer mit manueller Speicherverwaltung (new/delete) sind eine Quelle für Speicherlecks und doppelte Löschvorgänge. Fortgeschrittenes Refactoring beinhaltet die Einführung von RAII und Smart Pointern.



3.1. Analyse der Eigentümerschaft



Bevor Sie Pointer ändern, bestimmen Sie das Eigentumsmodell:

  • Exklusives Eigentum — verwenden Sie std::unique_ptr.

Blogs

Buchempfehlungen