Hintergrund:
Makros stammen aus der Sprache C und sind ein mächtiges Mittel zur Automatisierung wiederkehrender Codeabschnitte in der Vorverarbeitung. Ihre Verwendung in C++ brachte Flexibilität, aber auch viele verborgene Gefahren mit sich, aufgrund fehlender Typkontrolle und unklarer Funktionsweise des Präprozessors.
Problem:
Die Hauptgefahren der Verwendung von Makros:
Lösung:
In modernen C++-Standards wird empfohlen, Inline-Funktionen, Templates, constexpr, enum class sowie constexpr-Variablen anstelle von Makros zu verwenden.
Beispielcode:
// Schlecht: #define MAX(a, b) ((a) > (b) ? (a) : (b)) // Gut: template<typename T> constexpr T max(T a, T b) { return a > b ? a : b; }
Wesentliche Merkmale:
Kann ein Makro gefährlicher sein als eine Inline-Funktion?
Ja. Ein Makro unterliegt nicht den Regeln der Syntax und der Typen. Ein unerwartetes Ergebnis kann auftreten, wenn Parameter mit Nebeneffekten übergeben werden.
#define SQUARE(x) ((x) * (x)) int y = 5; int z = SQUARE(y++); // y wird zweimal inkrementiert!
Ist #include auch ein Makro?
Nein, #include ist eine Präprozessor-Direktive, aber die Verwendung von Makros und include ist verbunden: über ein Makro kann die Liste der eingebundenen Dateien geändert werden (extrem nicht empfohlen).
Kann man ein Makro wie eine normale Funktion debuggen?
Nein, der Debugger entfaltet das Makro und zeigt den bereits eingesetzten Text an, es gibt keine separaten benannten Entitäten.
Im alten Code wurden zahlreiche Berechnungs-Makros mit Effekten (z.B. Inkrement) definiert, was schwer fassbare Bugs bei der Nutzung neuer Funktionen nach sich zog.
Vorteile:
Nachteile:
Bei der Refaktorisierung wurden Makros durch template- und constexpr-Funktionen ersetzt, und enum class wurde anstelle von Makro-Flags verwendet.
Vorteile:
Nachteile: