wake-up-neo.com

konstante Variablen funktionieren nicht im Header

wenn ich meine konstanten varibles so in meinem Header definiere ...

extern const double PI = 3.1415926535;
extern const double PI_under_180 = 180.0f / PI;
extern const double PI_over_180 = PI/180.0f;

Ich erhalte folgende Fehlermeldung

1>MyDirectX.obj : error LNK2005: "double const PI" ([email protected]@3NB) already defined in main.obj
1>MyDirectX.obj : error LNK2005: "double const PI_under_180" ([email protected]@3NB) already defined in main.obj
1>MyDirectX.obj : error LNK2005: "double const PI_over_180" ([email protected]@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI" ([email protected]@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI_under_180" ([email protected]@3NB) already defined in main.obj
1>MyGame.obj : error LNK2005: "double const PI_over_180" ([email protected]@3NB) already defined in main.obj

aber wenn ich diese Konstanten aus der Kopfzeile entferne und in das Dokument stecke, wird die Kopfzeile wie folgt eingefügt ...

const double PI = 3.1415926535;
const double PI_under_180 = 180.0f / PI;
const double PI_over_180 = PI/180.0f;

Es klappt

Hat jemand eine Idee was ich falsch machen könnte?

Vielen Dank

53
numerical25

Das Problem ist, dass Sie define Objekte mit externer Verknüpfung in der Header-Datei haben. Wenn Sie diese Header-Datei in mehrere Übersetzungseinheiten aufnehmen, erhalten Sie erwartungsgemäß mehrere Definitionen desselben Objekts mit externer Verknüpfung, was ein Fehler ist.

Die richtige Vorgehensweise hängt von Ihrer Absicht ab.

  1. Sie können Ihre Definitionen in die Header-Datei einfügen, stellen Sie jedoch sicher, dass sie eine interne-Verknüpfung haben. 

    In C würde dies eine explizite static erfordern

    static const double PI = 3.1415926535; 
    static const double PI_under_180 = 180.0f / PI; 
    static const double PI_over_180 = PI/180.0f; 
    

    In C++ ist static optional (weil in C++ const Objekte standardmäßig eine interne Verknüpfung haben)

    const double PI = 3.1415926535; 
    const double PI_under_180 = 180.0f / PI; 
    const double PI_over_180 = PI/180.0f; 
    
  2. Oder Sie können nur nicht definierende Deklarationen in die Header-Datei einfügen und die Definitionen in eine (und nur eine) Implementierungsdatei einfügen

    Die Deklarationen in der Datei header müssen eine explizite extern und keine Initialisierung enthalten.

    extern const double PI; 
    extern const double PI_under_180; 
    extern const double PI_over_180; 
    

    und Definitionen in einer Implementierung -Datei sollten wie folgt aussehen

    const double PI = 3.1415926535; 
    const double PI_under_180 = 180.0f / PI; 
    const double PI_over_180 = PI/180.0f; 
    

    (explizite extern in den Definitionen ist optional, wenn die obigen Deklarationen den Definitionen in derselben Übersetzungseinheit vorangehen).

Welche Methode Sie wählen, hängt von Ihrer Absicht ab. 

Die erste Methode macht es dem Compiler einfacher, den Code zu optimieren, da er in jeder Übersetzungseinheit den tatsächlichen Wert der Konstante sehen kann. Gleichzeitig erhalten Sie konzeptionell in jeder Übersetzungseinheit separate, unabhängige, konstante Objekte. Beispielsweise wird &PI in jeder Übersetzungseinheit zu einer anderen Adresse ausgewertet.

Die zweite Methode erstellt wirklich global - Konstanten, d. H. Eindeutige konstante Objekte, die vom gesamten Programm gemeinsam genutzt werden. Beispielsweise wird &PI in jeder Übersetzungseinheit zur gleichen Adresse ausgewertet. In diesem Fall kann der Compiler jedoch nur die tatsächlichen Werte in einer und nur einer Übersetzungseinheit sehen, was die Optimierung beeinträchtigen könnte.


Ab C++ 17 erhalten Sie die dritte Option, die "das Beste aus beiden Welten" kombiniert: Inline-Variablen. Inline-Variablen können trotz externer Verknüpfung sicher in Header-Dateien definiert werden

inline extern const double PI = 3.1415926535; 
inline extern const double PI_under_180 = 180.0f / PI; 
inline extern const double PI_over_180 = PI/180.0f; 

In diesem Fall erhalten Sie ein benanntes konstantes Objekt, dessen Initialisierungswert in allen Übersetzungseinheiten sichtbar ist. Gleichzeitig besitzt das Objekt eine externe Verknüpfung, d. H. Es hat eine globale Adressidentität (&PI ist in allen Übersetzungseinheiten derselbe).

Zugegeben, so etwas ist möglicherweise nur für einige exotische Zwecke erforderlich (die meisten Anwendungsfälle in C++ für die erste Variante), aber die Funktion ist vorhanden.

125
AnT

extern bedeutet, dass die 'echte' Definition der Variablen an anderer Stelle ist, und der Compiler sollte darauf vertrauen, dass sich die Dinge zum Zeitpunkt der Verknüpfung verbinden. Die Definition der extern inline zu definieren, ist seltsam und ist das, was Ihr Programm beschwingt. Wenn Sie möchten, dass sie extern sind, definieren Sie sie genau einmal an anderer Stelle in Ihrem Programm.

9
Carl Norum

Die extern-Speicherklasse für sie ist fast sicher die Ursache des Problems, das Sie sehen. Wenn Sie es entfernen, wird der Code wahrscheinlich in Ordnung sein (zumindest in dieser Hinsicht).

Edit: Ich habe gerade bemerkt, dass Sie dies als C und C++ gekennzeichnet haben. In dieser Hinsicht unterscheiden sich C und C++ wirklich sehr stark (aber aus den Fehlermeldungen kompilieren Sie anscheinend als C++ und nicht als C). In C++ möchten Sie die Variable extern entfernen, da const-Variablen (standardmäßig) die Speicherklasse static besitzen. Das bedeutet, dass jede Quelldatei (Übersetzungseinheit) ihre eigene "Kopie" der Variablen erhält, und es gibt keinen Konflikt zwischen den Definitionen in verschiedenen Dateien. Da Sie (wahrscheinlich) nur die Werte verwenden, sie nicht als Variablen behandeln und mehrere "Kopien" haben, schadet nichts - keiner von ihnen wird Speicherplatz zugewiesen.

In C ist extern ziemlich unterschiedlich und das Entfernen der extern macht keinen wirklichen Unterschied, da sie standardmäßig extern sind. In diesem Fall müssen Sie die Variablen wirklich an genau einer Stelle initialisieren und sie extern im Header deklarieren. Alternativ können Sie die Speicherklasse static hinzufügen, die von C++ standardmäßig hinzugefügt wird, wenn /, wenn Sie die Variable extern aus dem Header entfernen.

5
Jerry Coffin

Viele falsche Antworten unten. Die richtigen sind die, die Sie dazu auffordern, die extern zu entfernen, da sellibitze in seinem Kommentar auch gesagt hat, dass sie richtig sind. 

Da diese als const deklariert sind, besteht kein Problem mit der Definition im Header. In C++ wird ein const für einen eingebauten Typ eingebettet, es sei denn, Sie versuchen, seine Adresse (einen Zeiger auf einen const) zu übernehmen. In diesem Fall wird es mit static linkage instanziiert. Möglicherweise erhalten Sie auch mehrere Instantiierungen in separaten Modulen, sofern Sie dies nicht erwarten Wenn alle Zeiger auf dieselbe Konstante dieselbe Adresse haben, ist dies kein Problem.

2
Clifford

Das Problem ist, dass Sie die Variablen in der Header-Datei initialisieren. Dadurch wird eine defining -Deklaration erstellt, die in jeder Datei wiederholt wird, die diesen Header enthält. Daher wird der Fehler bei der Mehrfachdefinition angezeigt.

Sie möchten eine nicht - definierende Deklaration (keine Initialisierung) in der Header-Datei und die definierende Deklaration in one der Implementierungsdateien.

2
John Bode

Sie müssen die Contants im Header deklarieren und anschließend in einer Ihrer Codedateien definieren. Wenn Sie sie nirgendwo deklarieren, tritt ein Linker-Fehler auf, wenn versucht wird, die Deklaration an die eigentliche Definition zu binden. Sie können auch mit #ifdef-Anweisungen arbeiten, um eine Definition im Header zu erhalten.

Vergewissern Sie sich, dass sie in einem Header deklariert sind, der von jedem, der sie benötigt, enthalten ist, und stellen Sie sicher, dass sie genau einmal definiert werden.

Jacob

1
TheJacobTaylor

Wenn Sie Konstanten in Headerdateien definieren möchten, verwenden Sie static const. Wenn Sie extern verwenden, kann sich der Linker über mehrere Definitionen beschweren, da jede Quelldatei einschließlich der Quelldatei Speicher für die Variable zur Verfügung stellt, wenn Sie einen Wert zuweisen.

1
Christoph

Es sieht so aus, als ob die Header-Datei mehrmals aufgenommen wird. Sie müssen Wachen hinzufügen.

Am oberen Rand jeder Header-Datei sollten Sie Folgendes haben:

#ifndef MY_HEADER_FILE_NAME_H
#define MY_HEADER_FILE_NAME_H

...

// at end of file
#endif

Wenn Sie g ++ oder MSVC verwenden, können Sie einfach Folgendes hinzufügen:

#pragma once

Oben in jeder Headerdatei, aber das ist nicht zu 100% portabel.

Sie sollten auch keine Konstanten in Header-Dateien definieren, sondern nur deklarieren:

// In header file
extern const int my_const;


// In one source file
const int my_const = 123;
1
Peter Alexander

Eine alte Frage, in der Tat, aber eine nützliche Antwort fehlt.

Es ist möglich, MSVC dazu zu bewegen, statische Konstanten in Headern zu akzeptieren, indem Sie diese einfach in eine "Dummy" -Klassenvorlage einschließen:

template <typename Dummy = int>
struct C {
     static const double Pi;
};

template <typename Dummy = int>
const double C<Dummy>::Pi = 3.14159;

Auf C <> :: PI kann jetzt von anderen Stellen aus zugegriffen werden. Keine Neudefinition beschwert sich; Konstante ist in jeder Compilation-Einheit direkt zugänglich, ohne die Link-Zeit zu optimieren. Makro kann ausgerollt werden, um diesen Ansatz weiter zu verbessern (auch wenn Makros böse sind).

0
oakad

bei der Deklaration von global const innerhalb des Headers hat jede Kompilierungseinheit, die diesen hader enthält, eigene Definitionen.

Wenn Sie diese wirklich im Header benötigen, sollten Sie sie wahrscheinlich als statisch deklarieren. 

0
lollinus