it-swarm.com.de

Definieren Sie konstante Variablen im C++ - Header

Ein Programm, an dem ich arbeite, hat viele Konstanten, die für alle Klassen gelten. Ich möchte eine Header-Datei "Constants.h" erstellen und alle relevanten Konstanten deklarieren können. Dann kann ich in meinen anderen Klassen einfach #include "Constants.h einfügen. 

Ich habe es gut mit #ifndef ... #define ... Syntax zu arbeiten. Ich würde jedoch die Konstanten const int... bevorzugen. Ich bin mir nicht ganz sicher, wie es geht. 

40
user1599559

Sie können einfach eine Reihe von const ints in einer Headerdatei definieren:

// Constants.h
#if !defined(MYLIB_CONSTANTS_H)
#define MYLIB_CONSTANTS_H 1

const int a = 100;
const int b = 0x7f;

#endif

Dies funktioniert, weil in C++ ein Name im Namespace-Bereich (einschließlich des globalen Namespaces), der explizit als const deklariert und nicht explizit extern deklariert ist, eine interne Verknüpfung hat. Diese Variablen verursachen also keine doppelten Symbole, wenn Sie Übersetzungseinheiten miteinander verknüpfen. Alternativ können Sie die Konstanten explizit als statisch deklarieren.

static const int a = 100;
static const int b = 0x7f;

Dies ist mit C besser kompatibel und für Benutzer, die mit C++ - Verknüpfungsregeln nicht vertraut sind, besser lesbar.

Wenn alle Konstanten ints sind, können Sie die Bezeichner auch als enum deklarieren.

enum mylib_constants {
    a = 100;
    b = 0x7f;
};

Alle diese Methoden verwenden nur einen Header und ermöglichen die Verwendung der deklarierten Namen als Kompilierungszeitkonstanten. Die Verwendung von extern const int und einer separaten Implementierungsdatei verhindert, dass die Namen als Kompilierungszeitkonstanten verwendet werden.


Beachten Sie, dass die Regel, die bestimmte Konstanten implizit zur internen Verknüpfung do macht, auf Zeiger angewendet wird, genau wie Konstanten anderer Typen. Das Schwierige dabei ist jedoch, dass das Markieren eines Zeigers als const eine etwas andere Syntax erfordert, die die meisten Leute verwenden, um Variablen anderer Typen zu konstanten. Sie müssen tun:

int * const ptr;

einen konstanten Zeiger machen, damit die Regel auf ihn angewendet wird.

Beachten Sie auch, dass dies ein Grund ist, weshalb ich es vorziehen möchte, const nach dem Typ konsistent zu setzen: int const anstelle von const int. Ich setze auch den * neben die Variable: d. H. int *ptr; anstelle von int* ptr;.

Ich mache solche Dinge gerne, weil sie den generellen Fall zeigen, wie C++ wirklich funktioniert. Die Alternativen (const int, int* p) sind nur speziell dafür gedacht, einige einfache Dinge lesbarer zu machen. Das Problem ist, dass die speziellen Alternativen, wenn Sie aus diesen einfachen Fällen aussteigen, aktiv irreführend sind.

Obwohl die früheren Beispiele die übliche Verwendung von const zeigen, würde ich tatsächlich empfehlen, dass die Leute sie so schreiben:

int const a = 100;
int const b = 0x7f;

und 

static int const a = 100;
static int const b = 0x7f;
56
bames53

Ich mag den Namespace besser für diesen Zweck.

Option 1 :

#ifndef MYLIB_CONSTANTS_H
#define MYLIB_CONSTANTS_H

//  File Name : LibConstants.hpp    Purpose : Global Constants for Lib Utils
namespace LibConstants
{
  const int CurlTimeOut = 0xFF;     // Just some example
  ...
}
#endif

// source.cpp
#include <LibConstants.hpp>
int value = LibConstants::CurlTimeOut;

Option 2 :

#ifndef MYLIB_CONSTANTS_H
#define MYLIB_CONSTANTS_H
//  File Name : LibConstants.hpp    Purpose : Global Constants for Lib Utils
namespace CurlConstants
{
  const int CurlTimeOut = 0xFF;     // Just some example
  ...
}

namespace MySQLConstants
{
  const int DBPoolSize = 0xFF;      // Just some example
  ...
}
#endif



// source.cpp
#include <LibConstants.hpp>
int value = CurlConstants::CurlTimeOut;
int val2  = MySQLConstants::DBPoolSize;

Ich würde niemals eine Klasse verwenden, um diese Art von HardCoded Const-Variablen aufzunehmen. 

24
Manikanda raj S

Sie sollten im Allgemeinen nicht z. const int in einer Headerdatei, wenn sie in mehreren Quelldateien enthalten ist. Das liegt daran, dass dann die Variablen einmal pro Quelldatei definiert werden (Übersetzungseinheiten im technischen Sinne), weil globale const-Variablen implizit statisch sind und somit mehr Speicherplatz beanspruchen als erforderlich.

Sie sollten stattdessen eine spezielle Quelldatei haben, Constants.cpp, die die Variablen tatsächlich definiert, und dann die Variablen als extern in der Header-Datei deklarieren lassen.

So etwas wie diese Headerdatei:

// Protect against multiple inclusions in the same source file
#ifndef CONSTANTS_H
#define CONSTANTS_H

extern const int CONSTANT_1;

#endif

Und das in einer Quelldatei:

const int CONSTANT_1 = 123;

Anstatt mehrere globale Variablen zu erstellen, können Sie auch eine Klasse mit öffentlichen statischen Konstanten erstellen. Es ist immer noch global, aber auf diese Weise ist es in eine Klasse gehüllt, so dass Sie wissen, woher die Konstante kommt und dass es eine Konstante sein soll.

Konstanten.h

#ifndef CONSTANTS_H
#define CONSTANTS_H

class GlobalConstants {
  public:
    static const int myConstant;
    static const int myOtherConstant;
};

#endif

Konstanten.cpp

#include "Constants.h"

const int GlobalConstants::myConstant = 1;
const int GlobalConstants::myOtherConstant = 3;

Dann kannst du das so verwenden:

#include "Constants.h"

void foo() {
  int foo = GlobalConstants::myConstant;
}
0

Es scheint, dass die Antwort von bames53 auf die Definition von ganzzahligen und nicht-ganzzahligen konstanten Werten in Namespace und class deklarationen erweitert werden kann, auch wenn sie in mehreren Quelldateien enthalten sind. Es ist nicht notwendig, die Deklarationen in eine Headerdatei zu schreiben, sondern die Definitionen in einer Quelldatei. Das folgende Beispiel funktioniert für Microsoft Visual Studio 2015, für z/OS V2.2 XL C/C++ unter OS/390 und für g ++ (GCC) 8.1.1 20180502 unter GNU/Linux 4.16.14 (Fedora 28). Beachten Sie, dass die Konstanten nur in einer einzigen Header-Datei deklariert/definiert werden, die in mehreren Quelldateien enthalten ist.

In foo.cc:

#include <cstdio>               // for puts

#include "messages.hh"
#include "bar.hh"
#include "Zoo.hh"

int main(int argc, const char* argv[])
{
  puts("Hello!");
  bar();
  Zoo();
  puts(Message::third);
  return 0;
}

In messages.hh:

#ifndef MESSAGES_HH
#define MESSAGES_HH

namespace Message {
  char const * const first = "Yes, this is the first message!";
  char const * const second = "This is the second message.";
  char const * const third = "Message #3.";
};

#endif

In bar.cc:

#include "messages.hh"
#include <cstdio>

void bar(void)
{
  puts("Wow!");
  printf("bar: %s\n", Message::first);
}

In Zoo.cc:

#include <cstdio>
#include "messages.hh"

void Zoo(void)
{
  printf("Zoo: %s\n", Message::second);
}

In bar.hh:

#ifndef BAR_HH
#define BAR_HH

#include "messages.hh"

void bar(void);

#endif

In Zoo.hh:

#ifndef Zoo_HH
#define Zoo_HH

#include "messages.hh"

void Zoo(void);

#endif

Dies ergibt die folgende Ausgabe:

Hello!
Wow!
bar: Yes, this is the first message!
Zoo: This is the second message.
Message #3.

Der Datentyp char const * const bedeutet einen konstanten Zeiger auf ein Array von konstanten Zeichen. Die erste const wird benötigt, weil (laut g ++) "ISO C++ das Konvertieren einer String-Konstante in 'char *' verbietet". Die zweite Variable const ist erforderlich, um Verbindungsfehler aufgrund mehrerer Definitionen der (dann unzureichend konstanten) Konstanten zu vermeiden. Ihr Compiler beschwert sich möglicherweise nicht, wenn Sie eine oder beide der consts weglassen, der Quellcode ist jedoch weniger portabel.

0
Louis Strous

C++ 17 inline variables

Diese großartige C++ 17-Funktion ermöglicht es uns:

  • verwenden Sie für jede Konstante nur eine einzige Speicheradresse
  • speichere es als constexpr: Wie deklariere ich constexpr extern?
  • tun Sie es in einer einzelnen Zeile aus einem Header

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_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 &notmain_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 Upstream .

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-Bezeichner":

6 Eine Inline-Funktion oder Variable mit externer Verknüpfung muss in allen Übersetzungseinheiten dieselbe Adresse haben.

cppreference https://en.cppreference.com/w/cpp/language/inline erklärt, dass wenn static nicht angegeben ist, eine externe Verknüpfung besteht.

Inline-Variablenimplementierung

Wir können beobachten, wie es umgesetzt wird mit:

nm main.o notmain.o

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 über u:

"u" Das Symbol ist ein eindeutiges globales Symbol. Dies ist eine Erweiterung von GNU zum Standardsatz von ELF-Symbolbindungen. Für ein solches Symbol sorgt der dynamische Linker dafür, dass Es gibt nur ein Symbol mit diesem Namen und Verwendungstyp.

wir sehen also, dass es dafür eine eigene ELF-Erweiterung gibt.

C++ 17 Standardentwurf zu const impliziert static

Dies ist das Zitat für das, was erwähnt wurde: https://stackoverflow.com/a/12043198/895245

C++ 17 n4659 Standardentwurf 6.5 "Programm und Verknüpfung":

3 Ein Name mit Namespace-Gültigkeitsbereich (6.3.6) hat eine interne Verknüpfung, wenn es sich um den Namen von handelt

  • (3.1) - eine Variablen-, Funktions- oder Funktionsvorlage, die explizit als statisch deklariert ist; oder,
  • (3.2) - Eine Nicht-Inline-Variable des nichtflüchtigen const-qualifizierten Typs, die weder extern noch .__ explizit deklariert ist. zuvor für externe Verknüpfung erklärt; oder
  • (3.3) - ein Datenmitglied einer anonymen Vereinigung.

Anhang C (informativ) Kompatibilität, C.1.2 Abschnitt 6: "Grundbegriffe" gibt die Gründe an, warum dies von C geändert wurde:

6.5 [auch 10.1.7]

Änderung: Ein explizit deklarierter und nicht explizit deklarierter Name des Dateibereichs hat interne Verknüpfung, während in C externe Verknüpfung vorhanden wäre.

Begründung: Da const-Objekte während der Übersetzung in C++ als Werte verwendet werden können, fordert diese Funktion auf Programmierer, um einen expliziten Initialisierer für jedes const-Objekt bereitzustellen. Mit dieser Funktion kann der Benutzer .__ eingeben. const-Objekte in Quelldateien, die in mehreren Übersetzungseinheiten enthalten sind.

Auswirkung auf das Originalmerkmal: Wechseln Sie zur Semantik des genau definierten Merkmals.

Konvertierungsschwierigkeit: Semantische Transformation.

Wie weit verbreitet: Selten.

Siehe auch: Warum impliziert const eine interne Verknüpfung in C++, wenn dies in C nicht der Fall ist?