Alternativen zur Verwendung von "#define" in C ++? Warum ist es verpönt?

8

Ich habe C ++ für weniger als ein Jahr entwickelt, aber in dieser Zeit habe ich mehrere Leute darüber reden hören, wie schrecklich #define ist. Jetzt merke ich, dass es vom Präprozessor statt vom Compiler interpretiert wird und somit nicht debuggt werden kann, aber ist das wirklich so schlimm?

Hier ist ein Beispiel (ungeprüfter Code, aber Sie erhalten die allgemeine Idee):

%Vor%
  1. Warum ist dieser Code schlecht?
  2. Gibt es eine Alternative zur Verwendung von #define ?
Joel 21.04.2012, 18:34
quelle

5 Antworten

8
  

Warum ist dieser Code schlecht?

Weil VERSION überschrieben werden kann und der Compiler es nicht sagt.

  

Gibt es eine Alternative zur Verwendung von #define?

%Vor%

oder

%Vor%     
Benjamin Lindley 21.04.2012, 18:40
quelle
10

Das eigentliche Problem ist, dass Defines von einem anderen Werkzeug als dem Rest der Sprache (dem Präprozessor) gehandhabt werden. Daher weiß der Compiler nichts davon und kann Ihnen nicht helfen, wenn etwas schief geht - wie die Wiederverwendung eines Präprozessornamens.

Betrachten wir den Fall von max , der manchmal als Makro implementiert wird. Daher können Sie den Bezeichner max nirgendwo in Ihrem Code verwenden. Irgendwo . Aber der Compiler wird es dir nicht sagen. Stattdessen wird Ihr Code schrecklich schief gehen und Sie haben keine Ahnung warum.

Nun, mit einiger Vorsicht kann dieses Problem minimiert (wenn nicht vollständig beseitigt) werden. Aber für die meisten Anwendungen von #define gibt es sowieso bessere Alternativen, so dass die Kosten-Nutzen-Rechnung verzerrt wird: ein geringer Nachteil für den no Nutzen überhaupt. Warum ein defektes Feature verwenden, wenn es keinen Vorteil bietet?

Also hier ist ein sehr einfaches Diagramm:

  1. Brauchen Sie eine Konstante? Verwenden Sie eine Konstante (keine Definition)
  2. Brauchen Sie eine Funktion? Verwenden Sie eine Funktion (keine Definition)
  3. Brauchen Sie etwas, das nicht mit einer Konstante oder einer Funktion modelliert werden kann? Verwenden Sie eine Definition, aber tun Sie es richtig.

Es "richtig" zu machen ist eine Kunst für sich, aber es gibt ein paar einfache Richtlinien:

  1. Verwenden Sie einen eindeutigen Namen. Alle Großbuchstaben, immer mit einem eindeutigen Bibliotheksbezeichner. %Code%? Aus. %Code%? Aus. Verwenden Sie stattdessen max und VERSION . Zum Beispiel verwenden Boost-Bibliotheken, große Benutzer von Makros, immer Makros, die mit MY_COOL_LIBRARY_MAX beginnen.

  2. Vorsicht vor der Auswertung. In der Tat ist ein Parameter in einem Makro nur Text, der ersetzt wird. Als Konsequenz wird MY_COOL_LIBRARY_VERSION gebrochen: Es könnte als BOOST_<LIBRARY_NAME>_ verwendet werden, was zu #define MY_LIB_MULTIPLY(x) x * x führt. Nicht was wir wollten. Um dagegen zu wehren, immer parenhesiere alle Verwendungen der Argumente (es sei denn, du weißt genau was du tust - Spoiler: du wahrscheinlich nicht t, sogar Experten bekommen dies oft alarmierend oft).

    Die richtige Version dieses Makros wäre:

    %Vor%

Aber es gibt immer noch viele Möglichkeiten, Makros fürchterlich falsch zu machen, und, um es noch einmal zu sagen, der Compiler wird dir hier nicht helfen.

    
Konrad Rudolph 21.04.2012 18:50
quelle
4

#define ist nicht von Natur aus schlecht, es ist einfach zu missbrauchen. Für etwas wie eine Versionszeichenkette funktioniert es gut, obwohl ein const char* besser wäre, aber viele Programmierer verwenden es für viel mehr als das. Die Verwendung von #define als typedef ist zum Beispiel dumm, wenn in den meisten Fällen ein typedef besser wäre. Es ist also nichts falsch mit #define -Anweisungen, und einige Dinge können nicht ohne sie gemacht werden. Sie müssen von Fall zu Fall bewertet werden. Wenn Sie einen Weg finden, um ein Problem zu lösen, ohne den Präprozessor zu verwenden, sollten Sie es tun.

    
Kyle 21.04.2012 18:40
quelle
2

Ich würde #define nicht verwenden, um eine Konstante zu definieren, die static oder noch besser verwendet %Code% %Code% ODER const int kMajorVer = 1;

Herb sutter hat hier einen exzellenten Artikel, der ausführlich erklärt, warum const int kMinorVer = 2; schlecht ist, und listet einige Beispiele auf, wo es eigentlich keinen anderen Weg gibt, um dasselbe zu erreichen: Ссылка .

Grundsätzlich wie bei vielen Dingen ist es in Ordnung, solange Sie es richtig verwenden, aber es ist leicht zu missbrauchen und Makrofehler sind besonders kryptisch und ein Bugger zum Debuggen.

Ich persönlich benutze sie für bedingten Debug-Code und auch Variant Datendarstellungen, die am Ende des Artikels sutter detailliert aufgeführt ist.

    
EdChum 21.04.2012 19:00
quelle
0

Im Allgemeinen ist der Präprozessor schlecht, weil er einen Zwei-Pass-Kompilierungsprozess erzeugt, der unsicher ist, schwer zu decodierende Fehlermeldungen erzeugt und zu schwer lesbarem Code führen kann. Sie sollten es nicht verwenden, wenn möglich:

%Vor%

Es gibt jedoch Fälle, in denen es unmöglich ist, das zu tun, was Sie ohne den Präprozessor machen wollen:

%Vor%     
Andrew Tomazos 21.04.2012 18:44
quelle