Ich möchte eine Konstante in C ++ definieren, die in mehreren Quelldateien sichtbar ist. Ich kann mir folgende Möglichkeiten vorstellen, um es in einer Header-Datei zu definieren:
#define GLOBAL_CONST_VAR 0xFF
int GLOBAL_CONST_VAR = 0xFF;
- Einige Funktion returing den Wert (zB
int get_GLOBAL_CONST_VAR()
) enum { GLOBAL_CONST_VAR = 0xFF; }
const int GLOBAL_CONST_VAR = 0xFF;
extern const int GLOBAL_CONST_VAR;
und in einer Quelldateiconst int GLOBAL_CONST_VAR = 0xFF;
Option (1) - ist definitiv nicht die Option, die Sie verwenden möchten
Option (2) - Definieren der Instanz der Variablen in jeder Objektdatei mithilfe der Header-Datei
Option (3) - IMO ist in den meisten Fällen über das Töten hinaus
Option (4) - in vielen Fällen möglicherweise nicht gut, da enum keinen konkreten Typ hat (C ++ 0X fügt die Möglichkeit hinzu, den Typ zu definieren)
In den meisten Fällen muss ich also zwischen (5) und (6) wählen. Meine Fragen:
- Was bevorzugen Sie (5) oder (6)?
- Warum ist (5) in Ordnung, (2) nicht?
Antworten:
(5) sagt genau das, was Sie sagen möchten. Außerdem kann der Compiler es die meiste Zeit optimieren. (6) Auf der anderen Seite lässt der Compiler es niemals wegoptimieren, da der Compiler nicht weiß, ob Sie es irgendwann ändern werden oder nicht.
quelle
extern const int ...
undconst int ...
sind beide konstant, nicht wahr?Entscheiden Sie sich auf jeden Fall für Option 5 - sie ist typsicher und ermöglicht dem Compiler die Optimierung (nehmen Sie die Adresse dieser Variablen nicht an :) Auch wenn sie sich in einem Header befindet - stecken Sie sie in einen Namespace, um eine Verschmutzung des globalen Bereichs zu vermeiden:
// header.hpp namespace constants { const int GLOBAL_CONST_VAR = 0xFF; // ... other related constants } // namespace constants // source.cpp - use it #include <header.hpp> int value = constants::GLOBAL_CONST_VAR;
quelle
header.hpp
in mehrere Quelldateien aufzunehmen.constexpr
Aufzählungen für solche Dinge geschrieben.(5) ist "besser" als (6), da es
GLOBAL_CONST_VAR
in allen Übersetzungseinheiten als integraler konstanter Ausdruck (ICE) definiert ist . Sie können es beispielsweise als Arraygröße und als Fallbezeichnung in allen Übersetzungseinheiten verwenden. Im Fall von (6)GLOBAL_CONST_VAR
wird ein ICE nur in der Übersetzungseinheit sein, in der er definiert ist, und erst nach dem Definitionspunkt. In anderen Übersetzungseinheiten funktioniert es nicht als ICE.Beachten Sie jedoch, dass (5) eine
GLOBAL_CONST_VAR
interne Verknüpfung ergibt , was bedeutet, dass die "Adressidentität" vonGLOBAL_CONST_VAR
in jeder Übersetzungseinheit unterschiedlich ist, dh&GLOBAL_CONST_VAR
, dass Sie in jeder Übersetzungseinheit einen anderen Zeigerwert erhalten. In den meisten Anwendungsfällen spielt dies keine Rolle. Wenn Sie jedoch ein konstantes Objekt mit einer konsistenten globalen "Adressidentität" benötigen, müssen Sie mit (6) fortfahren und die ICE-Ness der Konstanten in der Prozess.Wenn die ICE-Ness der Konstante kein Problem darstellt (kein integraler Typ) und die Größe des Typs größer wird (kein skalarer Typ), wird (6) normalerweise zu einem besseren Ansatz als (5).
(2) ist nicht in Ordnung, da
GLOBAL_CONST_VAR
in (2) standardmäßig eine externe Verknüpfung besteht. Wenn Sie es in eine Header-Datei einfügen, erhalten Sie normalerweise mehrere Definitionen vonGLOBAL_CONST_VAR
, was ein Fehler ist.const
Objekte in C ++ haben standardmäßig eine interne Verknüpfung, weshalb (5) funktioniert (und aus diesem Grund erhalten Sie, wie oben erwähnt,GLOBAL_CONST_VAR
in jeder Übersetzungseinheit eine separate, unabhängige Verknüpfung ).Ab C ++ 17 haben Sie die Möglichkeit zu deklarieren
inline extern const int GLOBAL_CONST_VAR = 0xFF;
in einer Header-Datei. Dies gibt Ihnen einen ICE in allen Übersetzungseinheiten (genau wie Methode (5)) zur gleichen Zeit, wobei die globale Adressidentität von beibehalten wird
GLOBAL_CONST_VAR
- in allen Übersetzungseinheiten hat er dieselbe Adresse.quelle
Wenn Sie C ++ 11 oder höher verwenden, versuchen Sie, Konstanten zur Kompilierungszeit zu verwenden:
constexpr int GLOBAL_CONST_VAR{ 0xff };
quelle
Wenn es eine Konstante sein soll, sollten Sie sie als Konstante markieren - deshalb ist 2 meiner Meinung nach schlecht.
Der Compiler kann die Konstante des Werts verwenden, um einige der mathematischen und tatsächlich andere Operationen, die den Wert verwenden, zu erweitern.
Die Wahl zwischen 5 und 6 - hmm; 5 fühlt sich einfach besser für mich an.
In 6) wird der Wert unnötigerweise von seiner Deklaration getrennt.
Normalerweise habe ich einen oder mehrere dieser Header, die nur Konstanten usw. definieren, und dann keine anderen "cleveren" Dinge - nette, leichte Header, die überall leicht eingefügt werden können.
quelle
So beantworten Sie Ihre zweite Frage:
(2) ist illegal, weil es gegen die One Definition Rule verstößt. Es definiert
GLOBAL_CONST_VAR
in jeder Datei, in der es enthalten ist, dh mehr als einmal. (5) ist legal, da es nicht der One Definition Rule unterliegt. JedesGLOBAL_CONST_VAR
ist eine separate Definition, lokal für die Datei, in der es enthalten ist. Alle diese Definitionen haben natürlich den gleichen Namen und Wert, aber ihre Adressen können unterschiedlich sein.quelle
C ++ 17
inline
VariablenMit dieser fantastischen C ++ 17-Funktion können wir:
constexpr
: Wie deklariere ich constexpr extern?main.cpp
#include <cassert> #include "notmain.hpp" int main() { // Both files see the same memory address. assert(¬main_i == notmain_func()); assert(notmain_i == 42); }
notmain.hpp
#ifndef NOTMAIN_HPP #define NOTMAIN_HPP inline constexpr int notmain_i = 42; const int* notmain_func(); #endif
notmain.cpp
#include "notmain.hpp" const int* notmain_func() { return ¬main_i; }
Kompilieren und ausführen:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o ./main
GitHub stromaufwärts .
Siehe auch: Wie funktionieren Inline-Variablen?
C ++ - Standard für Inline-Variablen
Der C ++ - Standard garantiert, dass die Adressen gleich sind. C ++ 17 N4659 Standardentwurf 10.1.6 "Der Inline-Spezifizierer":
cppreference https://en.cppreference.com/w/cpp/language/inline erklärt, dass, wenn
static
nicht angegeben, eine externe Verknüpfung besteht.Inline-Variablenimplementierung
Wir können beobachten, wie es implementiert wird mit:
was beinhaltet:
main.o: U _GLOBAL_OFFSET_TABLE_ U _Z12notmain_funcv 0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__ U __assert_fail 0000000000000000 T main 0000000000000000 u notmain_i notmain.o: 0000000000000000 T _Z12notmain_funcv 0000000000000000 u notmain_i
und
man nm
sagt überu
:Wir sehen also, dass es dafür eine dedizierte ELF-Erweiterung gibt.
Getestet auf GCC 7.4.0, Ubuntu 18.04.
quelle
const int GLOBAL_CONST_VAR = 0xFF;
weil es eine Konstante ist!
quelle
Das hängt von Ihren Anforderungen ab. (5) ist das Beste für die meisten normalen Verwendungszwecke, führt jedoch häufig dazu, dass in jeder Objektdatei ständig Speicherplatz belegt wird. (6) kann dies in Situationen umgehen, in denen es wichtig ist.
(4) ist auch eine gute Wahl, wenn Ihre Priorität darin besteht, sicherzustellen, dass niemals Speicherplatz zugewiesen wird, sondern natürlich nur für integrale Konstanten.
quelle
#define GLOBAL_CONST_VAR 0xFF // this is C code not C++ int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration) const int GLOBAL_CONST_VAR = 0xFF; // it is the best extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this
quelle