static const in Headern?



  • Reth schrieb:

    Nun habe ich folgendes Problem. Ich möchte eine globale Konstante vom Typ einer Struktur deklarieren und definieren.
    Die mache ich in einem .cc File, da ich gelesen habe, dass man globale Variablen mit externem Linking nur einmal anlegen darf.

    Das ist korrekt. Bzw. korrekt ist, dass man diese nur einmal _definieren_ darf (Btw: Es heißt "Linkage")

    Als ich sie in einem .h File anlegte, dass mit #ifdef vor doppeltem Gebrauch geschützt ist hat der Linker des g++ trotzdem gemeckert.

    Mit diesen include-Guards verhinderst du nur, dass das entsprechende .h File nicht innerhalb _einer_ Übersetzungseinheit doppelt eingebunden wird! D.h. wenn du bsp. den Header header.hpp in deine Module foo.cpp und bar.cpp inkludierst, wird er sowohl in dem Objektcode von foo als auch in dem Objektcode von bar auftauchen. (Da kann sich der Linker schon mal beschweren :))

    Also hab ich mir eine Datei Globals.cc gemacht, in diese habe ich dann folgendes geschrieben:

    static const struct TagItem tagEnd = {TAG_DONE, 0L};
    

    Es handelt sich hier um eine Struktur und definierte Typen des unterliegenden Betriebssystems.

    Jetzt hast du deine Struktur aber static deklariert. => Du kannst diese nur innerhalb dieser Datei ansprechen. Daraus resultieren die folgenden Fehler.

    Wie kann ich denn nun globale statische Konstanten in C++ anlegen und in Headern verwenden?

    Wenn du eine globale statische Variable anlegst, hat dieser interne Linkage. D.h. du kannst sie nur innerhalb der definierten ÜE benutzen. Du willst deine static-Variable aber nicht innerhalb der ÜE benutzen, in der sie definiert ist, sondern in einem Header, der von einem anderen Modul inkludiert wird. => Interne Linkage ist hier das Falsche... (Wenn ich dich korrekt verstanden habe)

    Wenn du also eine Variable in einem Modul _definieren_, und in einem Header, der in ein anderes Modul eingebunden wird, verwenden willst, kannst du dies z.B. so bewerkstelligen:

    Im Beispiel benutze ich den Header header.hpp und die Module definition.cpp (Hier wird die entsprechende Variable definiert) und main.cpp (Hier wird der Header header.hpp, der die Variable aus definition.cpp verwenden soll, inkludiert).

    // definition.cpp - Definition von tagEnd.
    const TagItem tagEnd = {TAG_DONE, 0L}; // TagItem muss hier natürlich bekannt sein...
    
    // header.hpp
    extern const TagItem tagEnd; // extern-Deklaration.
    
    // in diesem Header kannst du tagEnd benutzen...
    
    // main.cpp
    #include "header.hpp"
    // hier kannst du tagEnd ebenfalls benutzen...
    
    int main() { }
    

    Gruß Caipi



  • Vielen Dank Caipi!

    Das hat mein Verständnis der Materie etwas verbessert.

    Als ich in den Büchern gelesen habe, dass für statische Variablen nur einmal Speicher reserviert wird, wollte ich die Konstante global und statisch machen, da sie nur einmal im Speicher zu liegen braucht, auch wenn mehrere Einheiten sie verwenden.

    Wenn ich Dich recht verstanden habe, dann geht sowas gar nicht!?

    Ausser ich deklariere sie in Globals.h und definiere sie in Globals.cpp.
    Danach kann ich sie mit Globals::tagEnd doch irgendwie in nem anderen Header verwenden, wenn ich dort Globals.h inkludiere, oder?

    Ciao
    Reth



  • Reth schrieb:

    Als ich in den Büchern gelesen habe, dass für statische Variablen nur einmal Speicher reserviert wird, wollte ich die Konstante global und statisch machen, da sie nur einmal im Speicher zu liegen braucht, auch wenn mehrere Einheiten sie verwenden.

    Das ist nicht ganz korrekt. Korrekt ist, dass innerhalb einer Übersetzungseinheit nur einmal Speicher für eine als static deklarierte Variable reserviert wird.
    Für globale nicht-statische Objekte bzw. Variablen wird innerhalb des gesamten Programms (d.h. innerhalb aller ÜEs) nur einmal Speicher reserviert - nämlich zum Zeitpunkt der _Definition_ der selbigen. (Andernfalls beschwert sich der Linker).
    Ein weiterer Unterschied zwischen globalen als static deklarierten Objekten und globalen _nicht_ als static deklarierten Objekten, der mit obigen Gegebenheiten zusammenhängt, ist das Linkage. D.h. ob und wie sie mit anderen Übersetzungseinheiten verbunden werden.
    Merke:
    1.) Globale als static deklarierte Bezeichner haben internes Linkage, d.h., dass sie außerhalb der ÜE, in der sie definiert wurden, nicht ansprechbar sind.
    2.) Globale _nicht_ als static deklarierte Bezeichner haben externes Linkage, d.h., dass sie von jeder ÜE angesprochen werden können.

    Beispiel zu 1:
    (mit den Übersetzungseinheiten (Modulen) foo.cpp, bar.cpp und dem Header header.hpp)

    // header.hpp
    static const int static_var; // Definition einer konstanten statischen Variable vom Typ in.
    
    // foo.cpp:
    #include "header.hpp" // inkludiert den Header, der die static_var definiert.
    
    // Du kannst in diesem Modul nun static_var benutzen.
    
    int main() { ... }
    
    // bar.cpp:
    
    // In diesem Modul kannst du static_var aus foo.cpp _nicht_ verwenden, da es internes Linkage
    // besitzt. Falls du hier ebenfalls den Header header.hpp einbindest, enthält 
    // dieses Modul ebenfalls eine statische Variable namens static_var, die nicht 
    // mit dem gleichnamigen Bezeichner aus foo.cpp kollidiert. (Weil internes linkage).
    

    Beispiel zu 2: (siehe mein erster Post).

    Wenn ich Dich recht verstanden habe, dann geht sowas gar nicht!?

    Was geht nicht? Eine static deklarierte Variable in einer ÜE zu benutzen, in der sie nicht definiert wurde? Nein. Das geht nicht. Warum auch? Du kannst die Variable einfach nicht statisch deklarieren und hast externes Linkage.

    Ausser ich deklariere sie in Globals.h und definiere sie in Globals.cpp.
    Danach kann ich sie mit Globals::tagEnd doch irgendwie in nem anderen Header verwenden, wenn ich dort Globals.h inkludiere, oder?

    Wieso Globals::tagEnd? Ansonsten ist dein Gedankengang aber korrekt.

    Btw: AFAIK gibt es in der C++-FAQ auch einen Thread zu diesem Thema.

    Gruß Caipi



  • Also das Ganze funzt nicht so recht, irgendwas mach ich noch falsch!

    Habs nun so:

    Datei Globals.cc:

    #indeluce <Header für struct TagItem>
    const struct TagItem tagEnd = {TAG_END, 0L};
    

    Datei WindowDataC.h:

    ...
    private:
        extern const struct TagItem tagEnd;
    ...
    

    Nun meckert der G++ in dieser Stelle:
    storage class specified for field `tagEnd'

    Was will er mir denn damit sagen?
    Im make-File wird Globals.cc vor den Dateien übersetzt, welche WindowDataC.h verwenden!

    Was mach ich noch falsch?

    Ciao
    Reth



  • Reth schrieb:

    Was mach ich noch falsch?

    Falsch ist, dass du versuchst eine globale Variable in den Private-Bereich einer Klasse zu stecken. Das ist imho schon von der Denkweise der verkehrte Ansatz. Einerseits willst du eine konstante, globale Variable haben, willst diese dann aber in deiner Klasse wieder vor dem Zugriff von außen "verstecken", willst diese also in der Klasse wieder lokal machen.

    Gruß Caipi



  • Das kann sein. Wie gesagt bin absoluter C++ Newbie und dazu noch sehr sehr Java-lastig!

    Da gibts immer nur Klassen!

    Wie muss ich denn nun die externe Variable in WindowDataC.h verwenden, damit es funktioniert? Im Public-Teil oder gar außerhalb der Klassendefinition?

    Ciao
    Reth



  • Globale Variablen gehören immer außerhalb der Klassendefinition. (alles in der Definition ist ein Teil der jeweiligen Klasse)



  • Vielen Dank. Das habe ich gestern noch ausprobiert und es hat gefunzt, habs nur noch nicht gepostet.

    Aber um mal zitiert zu Fragen: Nun, warum ist das so? Sprich: Was ist das Konzept dahinter?

    Ciao
    Reth



  • Wie ich gerade schon gesagt habe - alles, was innerhalb der Klassendefinition steht, ist ein Teil der Klasse und deshalb nur im Zusammenhang dieser Klasse verwendbar. C hat (im Gegensatz zu Java) einen globalen Namensraum, in den alle Klassen-unabhängigen Definitionen gehören.



  • Naja, es müsste so heißen:

    // .hh
    
    class Foo {
    private: 
       const TagItem tagEnd; 
       ...
    }
    
    // .cc
    
    const Foo::TagItem tagEnd = {TAG_END, 0L};
    

    Das 'struct' kannste dir Sparen. Das war nur in C so, nicht aber in C++.



  • Welcher weg ist denn der richtige, oder gehen beide.

    Ich hab nun:

    Globals.cc
    
    const struct TagItem tagEnd = {TAG_END, 0L;};
    
    WindowDataC.h
    
    extern struct TagItem tagEnd;
    
    class ...
    

    Das wird mal compiliert.
    Brauch ich struct wirklich nicht mehr, auch wenn TagItem ne Struktur aus nem Header der Betriebssystemfunktionen ist?

    Ciao



  • In C++ brauchst du das wirklich nicht. Egal wie und wo deine struct declariert ist. Wenn der C++-compiler einen C-header list wird daraus c++ code. Das ist auch der Grund warum du Linker-Fehler bekommst wenn du einen c-header einbindest wo das extern "C" fehlt.
    Kurt


Anmelden zum Antworten