Statische Konstante vs. #define

Lesezeit: 11 Minuten

Statische Konstante vs define
Patrice Bernassola

Ist es besser zu verwenden static const anders als #define Präprozessor? Oder kommt es vielleicht auf den Kontext an?

Was sind Vorteile/Nachteile für jede Methode?

  • Scott Meyers behandelt dieses Thema sehr schön und gründlich. Sein Artikel Nr. 2 in “Effective C++ Third Edition”. Zwei Sonderfälle (1) statische Konstante wird innerhalb eines Klassenbereichs für klassenspezifische Konstanten bevorzugt; (2) Namensraum oder anonymer Gültigkeitsbereich const wird #define vorgezogen.

    – Erich

    18. Mai 2011 um 1:48 Uhr

  • Ich bevorzuge Enums. Denn es ist ein Hybrid aus beidem. Belegt keinen Platz, es sei denn, Sie erstellen eine Variable davon. Wenn Sie nur als Konstante verwenden möchten, ist enum die beste Option. Es hat Typsicherheit in C/C++11 std und auch eine perfekte Konstante. #define ist typunsicher, const benötigt Platz, wenn der Compiler es nicht optimieren kann.

    – siddhushingh

    1. Juli 2013 um 6:54 Uhr

  • Meine Entscheidung zur Verwendung #define oder static const (für Streicher) wird von angetrieben Initialisierung Aspekt (es wurde in den Antworten unten nicht erwähnt): Wenn die Konstante nur innerhalb einer bestimmten Kompilationseinheit verwendet wird, gehe ich mit static constsonst benutze ich #define – statische Auftragsinitialisierung vermeiden Fiasko isocpp.org/wiki/faq/ctors#static-init-order

    – Martin Dvorák

    3. Februar 2017 um 9:08 Uhr


  • Wenn const, constexpr oder enum oder jede Variation in Ihrem Fall funktioniert, dann ziehen Sie es vor #define

    – Phil1970

    28. August 2017 um 23:47 Uhr

  • @MartinDvorak “Vermeiden Sie das Fiasko der statischen Auftragsinitialisierung„Inwiefern ist das ein Problem für Konstanten?

    – Neugieriger

    15. Juni 2018 um 17:57 Uhr

Vor- und Nachteile dazwischen #defineS, consts und (was du vergessen hast) enums, je nach Verwendung:

  1. enumS:

    • nur für ganzzahlige Werte möglich
    • Probleme mit korrekten Bereichs-/Bezeichnerkonflikten wurden gut behandelt, insbesondere in C++ 11-Enumerationsklassen, in denen die Enumerationen für enum class X werden durch den Geltungsbereich disambiguiert X::
    • stark typisiert, aber auf eine ausreichend große Int-Größe mit Vorzeichen oder Vorzeichen, über die Sie in C++03 keine Kontrolle haben (obwohl Sie ein Bitfeld angeben können, in das sie gepackt werden sollen, wenn die Aufzählung ein Mitglied von struct/ ist class/union), während C++11 standardmäßig auf int kann aber vom Programmierer explizit gesetzt werden
    • kann die Adresse nicht übernehmen – es gibt keine, da die Enumerationswerte an den Verwendungsstellen effektiv inline ersetzt werden
    • stärkere Nutzungsbeschränkungen (z. B. Inkrementierung – template <typename T> void f(T t) { cout << ++t; } wird nicht kompiliert, obwohl Sie eine Aufzählung mit implizitem Konstruktor, Casting-Operator und benutzerdefinierten Operatoren in eine Klasse einschließen können)
    • der Typ jeder Konstante aus der umschließenden Aufzählung, also template <typename T> void f(T) Erhalten Sie eine eindeutige Instanziierung, wenn derselbe numerische Wert von verschiedenen Aufzählungen übergeben wird, die sich alle von tatsächlichen unterscheiden f(int) Instanziierung. Der Objektcode jeder Funktion könnte identisch sein (Adress-Offsets ignorieren), aber ich würde nicht erwarten, dass ein Compiler/Linker die unnötigen Kopien eliminiert, obwohl Sie Ihren Compiler/Linker überprüfen könnten, wenn Sie sich dafür interessieren.
    • selbst mit typeof/decltype kann man nicht erwarten, dass numeric_limits einen nützlichen Einblick in die Menge sinnvoller Werte und Kombinationen gibt (tatsächlich sind “legale” Kombinationen nicht einmal im Quellcode notiert, bedenken Sie enum { A = 1, B = 2 } – ist A|B “legal” aus der Sicht der Programmlogik?)
    • Der Typname des Enums kann an verschiedenen Stellen in RTTI, Compiler-Meldungen usw. erscheinen – möglicherweise nützlich, möglicherweise verschleiert
    • Sie können keine Aufzählung verwenden, ohne dass die Übersetzungseinheit den Wert tatsächlich sieht, was bedeutet, dass Aufzählungen in Bibliotheks-APIs die Werte im Header verfügbar machen müssen, und make und andere zeitstempelbasierte Neukompilierungstools lösen eine Client-Neukompilierung aus, wenn sie geändert werden (schlecht!)

  1. constS:

    • Konfliktprobleme mit richtiger Reichweite / Kennung werden gut behandelt
    • starker, einzelner, benutzerdefinierter Typ
      • Sie könnten versuchen, a zu “tippen”. #define ala #define S std::string("abc")aber die Konstante vermeidet die wiederholte Konstruktion unterschiedlicher Provisorien an jedem Verwendungspunkt
    • Komplikationen einer Definitionsregel
    • kann Adressen nehmen, konstante Referenzen zu ihnen erstellen usw.
    • am ähnlichsten einem Nicht-const Wert, der Arbeit und Auswirkungen minimiert, wenn zwischen den beiden gewechselt wird
    • -Wert kann in die Implementierungsdatei eingefügt werden, was eine lokalisierte Neukompilierung und nur Client-Links ermöglicht, um die Änderung aufzunehmen

  1. #defineS:

    • “globaler” Bereich / anfälliger für widersprüchliche Verwendungen, die zu schwer zu lösenden Kompilierungsproblemen und unerwarteten Laufzeitergebnissen führen können, anstatt zu vernünftigen Fehlermeldungen; Um dies zu mildern, ist Folgendes erforderlich:
      • lange, obskure und/oder zentral koordinierte Identifikatoren, und der Zugriff darauf kann nicht von implizit übereinstimmenden verwendeten/aktuellen/von Koenig nachgeschlagenen Namensräumen, Namensraum-Aliassen usw. profitieren.
      • Während die bewährte Best-Practice es zulässt, dass Bezeichner von Vorlagenparametern Großbuchstaben aus einem Zeichen (möglicherweise gefolgt von einer Zahl) sind, ist die andere Verwendung von Bezeichnern ohne Kleinbuchstaben herkömmlicherweise für Präprozessordefinitionen reserviert und wird von diesen erwartet (außerhalb der Betriebssystem- und C/C++-Bibliothek Kopfzeilen). Dies ist wichtig, damit die Verwendung von Präprozessoren im Unternehmensmaßstab überschaubar bleibt. Von Bibliotheken von Drittanbietern kann erwartet werden, dass sie sich daran halten. Wenn Sie dies beobachten, bedeutet dies, dass die Migration bestehender Konstanten oder Aufzählungen zu/von Definitionen eine Änderung der Großschreibung beinhaltet und daher eher Änderungen am Client-Quellcode als eine “einfache” Neukompilierung erfordert. (Persönlich schreibe ich den ersten Buchstaben von Aufzählungen groß, aber nicht Konstanten, also würde ich auch davon betroffen sein, zwischen diesen beiden zu wechseln – vielleicht ist es an der Zeit, das zu überdenken.)
    • mehr Operationen zur Kompilierzeit möglich: String-Literal-Verkettung, Stringifizierung (unter Berücksichtigung der Größe), Verkettung zu Bezeichnern
      • Nachteil ist, dass gegeben #define X "x" und etwas Client-Nutzung ala "pre" X "post"wenn Sie X zu einer zur Laufzeit änderbaren Variable statt zu einer Konstanten machen wollen oder müssen, erzwingen Sie Änderungen am Client-Code (anstatt nur eine Neukompilierung), während dieser Übergang von a aus einfacher ist const char* oder const std::string vorausgesetzt, sie zwingen den Benutzer bereits, Verkettungsoperationen (z "pre" + X + "post" zum string)
    • kann nicht verwenden sizeof direkt auf ein definiertes numerisches Literal
    • untypisiert (GCC warnt nicht im Vergleich zu unsigned)
    • Einige Compiler-/Linker-/Debugger-Ketten präsentieren möglicherweise nicht die Kennung, sodass Sie darauf beschränkt sind, “magische Zahlen” (Zeichenfolgen, was auch immer …) zu betrachten.
    • kann die Adresse nicht übernehmen
    • Der ersetzte Wert muss in dem Kontext, in dem #define erstellt wird, nicht zulässig (oder diskret) sein, da er an jedem Verwendungspunkt ausgewertet wird, sodass Sie auf noch nicht deklarierte Objekte verweisen können, abhängig von der “Implementierung”, die dies nicht muss voreingestellt sein, erstellen Sie “Konstanten” wie z { 1, 2 } die verwendet werden können, um Arrays zu initialisieren, oder #define MICROSECONDS *1E-6 etc. (bestimmt nicht empfehlenswert!)
    • einige besondere Dinge wie __FILE__ und __LINE__ kann in die Makrosubstitution eingebaut werden
    • können Sie auf Existenz und Wert prüfen #if Anweisungen zum bedingten Einschließen von Code (leistungsstärker als ein “if” nach der Vorverarbeitung, da der Code nicht kompilierbar sein muss, wenn er nicht vom Präprozessor ausgewählt wird), verwenden #undef-ine, neu definieren usw.
    • ersetzter Text muss exponiert werden:
      • in der Übersetzungseinheit, von der es verwendet wird, was bedeutet, dass Makros in Bibliotheken für die Client-Nutzung im Header stehen müssen, also make und andere zeitstempelbasierte Neukompilierungstools lösen eine Client-Neukompilierung aus, wenn sie geändert werden (schlecht!)
      • oder auf der Befehlszeile, wo noch mehr Sorgfalt erforderlich ist, um sicherzustellen, dass der Client-Code neu kompiliert wird (z. B. sollte das Makefile oder Skript, das die Definition bereitstellt, als Abhängigkeit aufgeführt werden).

Meine persönliche Meinung:

In der Regel verwende ich consts und betrachten sie als die professionellste Option für den allgemeinen Gebrauch (obwohl die anderen eine Einfachheit haben, die diesen alten faulen Programmierer anspricht).

  • Tolle Antwort. Eine kleine Kleinigkeit: Ich verwende manchmal lokale Aufzählungen, die überhaupt nicht in Headern enthalten sind, nur um den Code klarer zu machen, wie in kleinen Zustandsmaschinen und so. Sie müssen also nicht immer in Kopfzeilen stehen.

    – kert

    9. Juni 2014 um 15:46 Uhr

  • Die Vor- und Nachteile werden vermischt, ich würde mich sehr über eine Vergleichstabelle freuen.

    – Unbekannt123

    25. April 2019 um 3:49 Uhr


  • @Unknown123: Fühlen Sie sich frei, einen zu posten – es macht mir nichts aus, wenn Sie irgendwelche Punkte abreißen, die Sie von hier aus für würdig halten. Prost

    – Toni Delroy

    25. April 2019 um 19:57 Uhr

Statische Konstante vs define
TED

Ich persönlich verabscheue den Präprozessor, also würde ich immer mitgehen const.

Der Hauptvorteil zu a #define ist, dass es keinen Speicher benötigt, um es in Ihrem Programm zu speichern, da es wirklich nur einen Text durch einen Literalwert ersetzt. Es hat auch den Vorteil, dass es keinen Typ hat, sodass es für jeden ganzzahligen Wert verwendet werden kann, ohne Warnungen zu erzeugen.

Vorteile von “constDas liegt daran, dass sie bereichsabhängig sind und in Situationen verwendet werden können, in denen ein Zeiger auf ein Objekt übergeben werden muss.

Ich weiß nicht genau, worauf du hinaus willst mit dem “static” Teil aber. Wenn Sie global deklarieren, würde ich es in einen anonymen Namespace einfügen, anstatt es zu verwenden static. Zum Beispiel

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}

  • Schnur Konstanten sind insbesondere eine von denen, die vom Sein profitieren könnten #defined, zumindest wenn sie als “Bausteine” für größere Zeichenkettenkonstanten verwendet werden können. Siehe meine Antwort für ein Beispiel.

    – Ant

    28. Oktober 2009 um 14:10 Uhr

  • Die #define Der Vorteil, keinen Speicher zu verwenden, ist ungenau. Die “60” im Beispiel muss irgendwo gespeichert werden, egal ob static const oder #define. Tatsächlich habe ich Compiler gesehen, bei denen die Verwendung von #define einen massiven (schreibgeschützten) Speicherverbrauch verursachte und statische Konstante keinen unnötigen Speicher verbrauchte.

    – Gilad Naor

    27. Juli 2010 um 8:10 Uhr

  • Ein #define ist so, als ob Sie es eingetippt hätten, also kommt es definitiv nicht aus dem Gedächtnis.

    – der Pfarrer

    24. September 2011 um 19:52 Uhr

  • @theReverend Sind Literalwerte irgendwie davon ausgenommen, Maschinenressourcen zu verbrauchen? Nein, sie könnten sie nur auf unterschiedliche Weise verwenden, vielleicht erscheint es nicht auf dem Stack oder Heap, aber irgendwann wird das Programm zusammen mit allen darin kompilierten Werten in den Speicher geladen.

    – Quietschend

    3. Juli 2013 um 1:07 Uhr

  • @gilad-naor, Sie haben im Allgemeinen Recht, aber kleine ganze Zahlen wie 60 können manchmal tatsächlich eine Art partielle Ausnahme sein. Einige Befehlssätze haben die Fähigkeit, Ganzzahlen oder eine Teilmenge von Ganzzahlen direkt im Befehlsstrom zu codieren. Zum Beispiel fügen MIPs unmittelbar hinzu (cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/addi.html). In diesem Fall könnte man wirklich sagen, dass eine #definierte Ganzzahl keinen Platz verbraucht, da sie in der kompilierten Binärdatei ein paar freie Bits in Anweisungen belegt, die ohnehin vorhanden sein mussten.

    – ahcox

    25. Juni 2015 um 15:23 Uhr

Statische Konstante vs define
Ameise

Wenn dies eine C++-Frage ist und erwähnt wird #define alternativ geht es um “globale” (dh Dateibereichs-) Konstanten, nicht um Klassenmitglieder. Wenn es um solche Konstanten in C++ geht static const ist überflüssig. In C++ const haben standardmäßig eine interne Verknüpfung und es macht keinen Sinn, sie zu deklarieren static. Es geht also wirklich darum const vs. #define.

Und schließlich in C++ const istvorzuziehen. Zumindest, weil solche Konstanten typisiert und gültig sind. Es gibt einfach keine Gründe für eine Bevorzugung #define über constbis auf wenige Ausnahmen.

String-Konstanten, BTW, sind ein Beispiel für eine solche Ausnahme. Mit #defined String-Konstanten kann man die Verkettungsfunktion zur Kompilierzeit von C/C++-Compilern verwenden, wie in

#define OUT_NAME "output"
#define LOG_EXT ".log"
#define TEXT_EXT ".txt"

const char *const log_file_name = OUT_NAME LOG_EXT;
const char *const text_file_name = OUT_NAME TEXT_EXT;

PS Nochmal, nur für den Fall, wenn es jemand erwähnt static const als Alternative zu #define, bedeutet dies normalerweise, dass sie über C und nicht über C++ sprechen. Ich frage mich, ob diese Frage richtig getaggt ist …

  • einfach keine Gründe, #define zu bevorzugen“Über was? Statische Variablen, die in einer Header-Datei definiert sind?

    – Neugieriger

    15. Juni 2018 um 18:06 Uhr

#define kann zu unerwarteten Ergebnissen führen:

#include <iostream>

#define x 500
#define y x + 5

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

Gibt ein falsches Ergebnis aus:

y is 505
z is 510

Wenn Sie dies jedoch durch Konstanten ersetzen:

#include <iostream>

const int x = 500;
const int y = x + 5;

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

Es gibt das richtige Ergebnis aus:

y is 505
z is 1010

Das ist weil #define ersetzt einfach den Text. Da dies die Reihenfolge der Operationen ernsthaft durcheinander bringen kann, würde ich empfehlen, stattdessen eine konstante Variable zu verwenden.

Die Verwendung einer statischen Konstante ist wie die Verwendung anderer konstanter Variablen in Ihrem Code. Dies bedeutet, dass Sie nachverfolgen können, woher die Informationen stammen, im Gegensatz zu einem #define, das einfach im Code im Vorkompilierungsprozess ersetzt wird.

Vielleicht möchten Sie für diese Frage einen Blick in die C++ FAQ Lite werfen:
http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

1646977814 233 Statische Konstante vs define
ROTER WEICHER ADAIR

  • Eine statische Konstante ist typisiert (sie hat einen Typ) und kann vom Compiler auf Gültigkeit, Neudefinition etc. geprüft werden.
  • a #define kann undefiniert beliebig redefiniert werden.

Normalerweise sollten Sie statische Konstanten bevorzugen. Es hat keinen Nachteil. Der Prprozessor sollte hauptsächlich für die bedingte Kompilierung verwendet werden (und manchmal vielleicht für wirklich schmutzige Tricks).

Definieren von Konstanten mithilfe der Präprozessordirektive #define wird nicht empfohlen, sich nicht nur in zu bewerben C++aber auch drin C. Diese Konstanten haben nicht den Typ. Selbst in C Verwendung vorgeschlagen wurde const für Konstanten.

990020cookie-checkStatische Konstante vs. #define

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy