Projekt mit vielen Dateien



  • code.cpp (inkludiert exp_mouse.h und exp_game.h)

    exp_mouse.h
    exp_mouse.cpp

    exp_game.h
    exp_game.cpp

    Die beiden Headerdateien deklarieren die Klassen game und mouse.
    Die Methoden dieser Klassen stehen in den gleichnamigen cpp-Dateien.

    1. Die beiden cpp-Dateien müssen doch die gleichnamigen Header-Dateien
    inkludieren oder nicht?

    2. Wenn ich die Header-Dateien in die beiden cpp-Dateien inkludiere, dann bekomme ich trotzdem noch 2 Fehlermeldungen:

    error LNK2005: "class Game game" (?game@@3VGame@@A) bereits in code.obj definiert

    fatal error LNK1169: Ein oder mehrere mehrfach definierte Symbole gefunden

    Was mache ich falsch?



  • Du mußt deine Header vor Mehrfach-Inkludierung schützen:

    // header.h
    #ifndef HEADER_H_INCLUDED // denk dir hier selbst was aus, aber ohne __ am Anfang
    #define HEADER_H_INCLUDED
    
    class abcxyz { ... };
    #endif // HEADER_H_INCLUDED
    


  • Ich schütze die Header-Dateien ja schon vor mehrfacher Verwendung, aber trotzdem!

    Diese Header-Datei ist doch korrekt oder nicht:

    //========================================================
    //== class Game encapsulates data for game organisation
    //========================================================
    
    #ifndef EXP_GAME_H
    #define EXP_GAME_H
    
    enum DIRECTION{s, se, e, ne, n, nw, w, sw};
    
    class Game
    {
    private:
        unsigned delay;
        DIRECTION direction;
    public:
    
        Game();
        void SetDelay(unsigned d);
        unsigned& GetDelay();
        DIRECTION GetDirection();
        void SetDirection(DIRECTION d);
        void TurnLeft();
        void TurnRight();
    
    } game;
    
    #endif
    

    Ist das korrekt, dass ich am Ende der Klassendeklaration schon ein Game-Objekt erzeuge, obwohl die Methoden noch nicht in dieser Datei implementiert worden sind?



  • Ahso. Du solltest in einer Header-Datei grundsätzlich keine Variablen anlegen. Du kannst sie als extern deklarieren, und dann die Initialisierung in eine cpp-Datei packen, da sonst zwar der Compiler zufrieden ist (darauf beziehen sich die include-guards), aber der Linker trotzdem das Symbol mehrfach zu Gesicht bekommt - einmal für jede cpp-Datei, die diese Variable (indirekt über den Header) anlegt.



  • Original erstellt von <MC>:
    Aber wenn ich die Header-Dateien vor Mehrfach-Einbundung schütze, dann wird doch auch nur ein Game-Objekt game erzeugt und alles sollte laufen images/smiles/icon_confused.gif

    Hi,

    nein, du schützt es nur pro Modul vor Mehrfacheinbindung, jede CPP-Datei "vergisst" wieder die defines der vorherigen. Du musst dir einfach klarmachen, das der Complier für jedes CPP-File einmal aufgerufen wird. Der Compiler erzeugt ja die Objekt-Files, die der Linker dann zusammenfügt. Du hast jetzt mehrere Objekt-Files, die dieselbe Variable deklarieren, da weiss dann der Linker nicht mehr weiter.

    Tschau
    Frank



  • Mache dich mit dem Konzept der Übersetzungseinheiten vertraut. Eine Übersetzungseinheit ist das, was ein Compiler in einem Rutsch in ein Objectfile übersetzt (grob gesprochen), d.h. ein .cpp-File plus alle Includes. Du hast 3 Übersetzungseinheiten, code.cpp, exp_mouse.cpp und exp_game.cpp. Die Compilierung läuft jetzt so ab, dass der Preprozessor zuerst die cpp's durch den Fleischwolf dreht -- Ausdrücke werden vereinfacht, Makros ersetzt, und Header eingefügt. Dann geht das ganze an den Compiler und er erstellt ein Object-File. Wenn die drei Compilierungsvorgänge fertig sind, werden die Objects zusammengelinkt.
    Die Include-Guards schützen dich davor, innerhalb einer Übersetzungseinheit eine Headerdatei mehrfach einzubinden, aber nicht davor, dass das innerhalb des ganzen Projekts mehrfach geschieht -- das wär ja auch gar nicht im Sinne des Erfinders, denn du brauchst ja die Deklarationen der Klassen in der jeweiligen ÜE (keine Lust mehr das auszuschreiben images/smiles/icon_wink.gif).
    Eine Variable ist jedoch keine Deklaration, sondern eine Definition (da Speicher reserviert wird). Die Variable steht als Symbol in dem entsprechenden Object-File. Wenn die in einer Headerdatei definiert worden ist, ist sie auch in jeder ÜE definiert, die diese Headerdatei einbindet. Das heißt, dass am Ende der Linker mehrmals das gleiche Symbol sieht, und dann aussteigt.
    Jedes Linker-Symbol darf im ganzen Projekt nur ein einziges Mal definiert werden. Das heißt, die Definition der Variable gehört in eine CPP-Datei, die Deklaration in eine Header-Datei.

    Und so schreibt man das:

    // game.h
    #ifndef GAME_H
    #define GAME_H
    class Game { ... };
    extern Game game; // Deklaration!
    #endif
    
    // game.cpp
    #include "game.h"
    Game game;   // Definition!
    
    // main.cpp
    #include "game.h"
    int main() {
        game.do_something(); // game ist als globales Objekt verfügbar
        return 0;
    }
    

Anmelden zum Antworten