Verständnisproblem Klassen in C++



  • Hallo,

    ich lerne gerade C++ als Autodidakt anhand von Tutorials im Internet und habe leider ein Verständnisproblem bei Klassen.

    Ich habe ein schon etwas größeres Projekt programmiert, und es läuft fein wenn alle Klassendefinitionen samt Methodensourcecode in der Main.cpp - Datei sind.
    Ich habe versucht alles OOP-mässig schön in einen Abhängigkeitsbaum den ich (noch) durchblicke zu organisieren.

    Also z.B.:

    #ifndef XYZ_H
    #define XYZ_H
    class xyz : public abc
    {...};
    #endif
    

    Lagere die Klassendefinitionen (inklusive Include-Guards) in eine xyz.h - Datei aus klappts auch noch.

    Wenn ich aber den Methodensourcecode einer Klasse in die entsprechende xyz.cpp Datei auslagere (welche die xyz.h natürlich includiert) bekomme ich sehr viele Fehler vom Linker.

    z.B. der Typ vektor sei nicht bekannt, obwohl ich die "#include <vector>" im main.cpp noch VOR dem inkludieren der xyz.h einbinde - sollte also bekannt sein.
    Oder aber Klassenvariablen der Vaterklasse sind plötzlich nicht mehr bekannt und solche Scherze.
    Ich bin so ratlos, dass ich noch nichtmal weiß wo und über welches Thema ich nachlesen muss. (Vielleicht Namespaces?)

    Naja, mich störts wenig, dass alles in einer Sourcedatei ist, wird aber halt jetzt so langsam etwas unübersichtlich.

    Welches (Grund)wissen fehlt mir um Teile des Codes auszulagern?

    Gruss
    R

    PS: Nutze Mingw 2.05 under Windows



  • Auch in Headerdateien solltest du andere benötigte Header includen, dann sollte das eigentlich kein Problem mehr sein.



  • Einfache Logik: Was nutzt der Include in main.cpp VOR Deiner xyz.h, wenn der Compiler gerade garnicht die main.cpp sondern die xyz.cpp bearbeitet? 😉



  • Wenn das stimmt, dann ist die #include Anweisung nicht einfach ein Sourcecode-Platzhalter der beim Kompilieren einfach den entsprechenden Sourcecode an die Stelle setzt wo die #include Anweisung ist?

    Und wenn ja, dauert das Compilieren dann nicht ewig länger, da für jede einzelne Klasse diverse Bibliotheken doppelt und dreifach inkludiert werden müssen?

    Hier bei mir also pro Klassendefinition jeweils die strings, vector, iostream plus die jeweiligen Vaterklassendefinitionen?



  • also ich habe es gerade ausprobiert - klappt nicht.

    --------------------Configuration: nn - Debug--------------------
    Compiling source file(s)...
    main.cpp
    inventory.cpp
    inventory.cpp:1: error: syntax error before ::' token inventory.cpp:7: error: syntax error before<=' token
    inventory.cpp:7: error: syntax error before `++' token
    inventory.cpp:12:2: warning: no newline at end of file

    nn.exe - 3 error(s), 1 warning(s)

    relevanter source in main:

    #include <iostream>
    #include <windows.h>
    #include <string>
    #include <vector>
    #include "item.h"
    #include "etc_item.h"
    #include "weapon.h"
    #include "armor.h"
    #include "inventory.h"
    

    relevanter source in inventory.h:

    #ifndef INVENTORY_H
    #define INVENTORY_H
    #include <iostream>
    #include <windows.h>
    #include <string>
    #include <vector>
    #include "item.h"
    #include "etc_item.h"
    #include "weapon.h"
    #include "armor.h"
    
    class inventory {
    

    komplette inventory.cpp:

    void inventory::stats()
    {
    	//etc_items :
    	if (etc_items.size()==0) {cout<<"Nixdrin"<<endl;}
    	else
    	{
    	for(unsigned int i=0;i<=etc_items.size()-1;i++)  //search for existence of the stackable item
    	{
    		cout<<etc_items[i].amount<<" Einheiten der ID "<<etc_items[i].etc_item_id<<endl;
    	};
    	}
    }
    

    😞 Ich verstehe nicht, was in Zeile 1 hier falsch sein soll...



  • bololo77 schrieb:

    Wenn das stimmt, dann ist die #include Anweisung nicht einfach ein Sourcecode-Platzhalter der beim Kompilieren einfach den entsprechenden Sourcecode an die Stelle setzt wo die #include Anweisung ist?

    Und wenn ja, dauert das Compilieren dann nicht ewig länger, da für jede einzelne Klasse diverse Bibliotheken doppelt und dreifach inkludiert werden müssen?

    Ja, ist so, und es dauert auch länger. Dafür gibt es dann vorkompilierte Header.

    Wegen deinen Fehlern:
    Zeig mal die ganze inventory.h. Evtl. das Semikolon nach der Klassendefinition vergessen?



  • Ich sagte: Wenn er xyz.cpp bearbeitet, interessiert ihn nicht was in main.cpp steht.

    Wieso wird deshalb doppelt und dreifach includet? Es wird in main.cpp einmal includet und es wird in xyz.cpp einmal includet. Beides ist erforderlich damit er in beiden Dateien die Deklarationen kennt.

    Du kompilierst die Files doch unabhängig, wie soll ein include in main.cpp Auswirkungen auf andere Compile-Einheiten haben?



  • #ifndef INVENTORY_H
    #define INVENTORY_H
    class inventory
    	{
    	public:
    	std::vector<etc_item> etc_items;
    	std::vector<weapon> weapons;
    	inventory(){}   //konstruktor
    	unsigned int weight();
    	bool isfull();
    	void stats();
    	short int add_etc (unsigned long itid, unsigned short int how_much);	
    	bool has_etc(unsigned long itid, unsigned short int how_much);
    	bool remove_etc(unsigned long itid, unsigned short int how_much);
    };
    #endif
    

    Neku, also hier ist die komplette inventory.h

    Lord Jaxoms Ausführungen verstehe ich leider nicht... So wie ich es verstehe muss ich die strings, iostream, vektor etc.p.p in _jeder_ meiner .h Dateien includieren, da ich die dort brauche. Und ich hab ja nicht nur die main und die inventory. Das sind jetzt schon 10 und es werden noch viel viel mehr.
    Das meinte ich mit doppelt und dreifach.

    Naja, ich denke ich packs wieder alles in die main.cpp - Dann hab ich gar keine Probleme mehr und alles wird nur einmal ge-includet, und ich habe keine Syntax Errors mehr (wirklich identisch:copy and paste ohne die includeguards, und die Errors sind weg) . 😞

    Gruss
    R



  • Ich habe auch ein Projekt mit ca. 380 cpp/hpp-Dateien. Dann dauert das komplette Kompilieren halt eine Weile, und? Bei Visual Studio werden doch normalerweise ohnehin nur geänderte Source-Dateien kompiliert.
    Wenn ich das alles in eine Datei packe wird's übel 😛

    Den Grund für die Syntaxfehler sehe ich jetzt nicht. Sind es immernoch die gleichen Fehler, wenn du in der inventory.cpp die Includes auskommentierst?

    PS: Es gibt hier einen C++-Highlighter 😉 [cpp][/cpp]



  • Es macht keinen Unterschied.
    Ob ich

    #include <iostream>
    #include <windows.h>
    #include <string>
    #include <vector>
    #include "item.h"
    #include "etc_item.h"
    #include "weapon.h"
    #include "armor.h"
    

    in die .h oder die .cpp schreibe immer das gleiche. Und auch bei allen Klassen wo ich es versucht hab.

    packe ich es in beide (also .cpp und .h) dann bekomme ich >200 Errors.
    Naja, dafür das ich erst ein paar Tage C++ lerne, bin ich ja schon weit gekommen. 🙂

    Ich denke mir fehlt da einfach einiges an Grundverständnis der Sprache...

    Gruss
    R.



  • bololo77 schrieb:

    Es macht keinen Unterschied.
    Ob ich

    #include <iostream>
    #include <windows.h>
    #include <string>
    #include <vector>
    #include "item.h"
    #include "etc_item.h"
    #include "weapon.h"
    #include "armor.h"
    

    in die .h oder die .cpp schreibe immer das gleiche. Und auch bei allen Klassen wo ich es versucht hab.

    packe ich es in beide (also .cpp und .h) dann bekomme ich >200 Errors.
    Naja, dafür das ich erst ein paar Tage C++ lerne, bin ich ja schon weit gekommen. 🙂

    Ich denke mir fehlt da einfach einiges an Grundverständnis der Sprache...

    Gruss
    R.

    Ich schrieb "auskommentieren" 😉 (und zwar die Includes in der .cpp)



  • Erstmal vielen Dank für dein Verständnis einem blutigen Anfänger gegenüber.

    Also die 3 Syntaxfehler von eben kommen wenn ich die include-Anweisungen weder in der inventory.cpp noch in der inventory.h habe.
    Die gleichen Fehler kommen auch wenn ich die include-Anweisungen entweder in der .h ODER in der .cpp habe. Habe ich die includes in _beiden_ kommen >200 Fehler.

    Gruss
    R



  • Tut mir leid, bei den paar Zeilen Code in deiner .cpp kann ich mir die Fehler nicht erklären. Was ist das eigentlich für ein Compiler und für eine IDE?



  • hey hast du die "inventory.h" auch in die "inventory.cpp" includet?
    das sah nämlich nicht so aus...



  • Ich bat ihn ja darum, die Includes in der .cpp zu entfernen, um zu sehen, ob die Syntaxfehler die Schuld der Includes sind oder nicht. Offensichtlich sind sie es nicht.



  • Vielleicht nochmal was zum Vorgehen generell, vielleicht wird es dann etwas klarer für den blutigen Anfänger 😉

    Wenn Du Klassen bastelst, bei der die Trennung erfolgen soll, solltest Du alles in die .h includen, was in der .h auch gebraucht wird. Deklarierst Du in der Klasse ein std::string name, dann includest Du <string>. Bei einem vector<weapon> includest Du <vector> sowie Deine .h für die Klasse weapon. (Wenn Du weiter Fortgeschritten bist kannst Du etwas davon einsparen durch Forward Deklaration, aber da denk besser noch nicht drüber nach)

    Natürlich kannst Du das auch erschlagen, indem Du die benötigten Header alle zuvor in der .cpp includest, aber das ist sehr schlechter Stil, denn ich (hypothetisch natürlich) als Anwender Deiner Klassen möchte nicht dafür sorgen müssen die Header zu includen, die Du brauchst.

    Header, die in der Klassendeklaration nicht gebraucht werden, sondern erst im Code der Klasse in der .cpp-Datei, solltest Du natürlich erst in der .cpp-Datei includen. Damit ersparst Du dem Anwender, der nur die .h includet und später eine fertige Bibliothek linkt, und letztlich Dir selbst Symboltabellenverschmutzung (und Compilierungszeit).

    Also, geh Deine Header mal in Ruhe durch und include in ihnen alles was auch im Header benutzt wird. Dann das gleiche für die Sourcefiles. Mach Dir keine Sorgen über doppelte Includes, wenn Du also in Deiner .cpp-Datei "weapon.h" includest, und neue Vectoren nutzt, verlasse Dich nicht auf die Tatsache dass "weapons.h" <vector> includet, include <vector> lieber selbst in der .cpp noch mal.

    Kleines Beispiel anhand der Inventory-Klasse.

    /* inventory.h */
    #ifndef INVENTORY_H
    #define INVENTORY_H
    
    #include <vector>
    #include "etc_item.h" // Die Datei in der Deine Klasse etc_item enthalten ist
    #include "weapon.h" // Die Datei in der Deine Klasse weapon enthalten ist
    
    class inventory
        {
        public:
        std::vector<etc_item> etc_items;
        std::vector<weapon> weapons;
        inventory(){}   //konstruktor
        unsigned int weight();
        bool isfull();
        void stats();
        short int add_etc (unsigned long itid, unsigned short int how_much);   
        bool has_etc(unsigned long itid, unsigned short int how_much);
        bool remove_etc(unsigned long itid, unsigned short int how_much);
    };
    #endif
    
    /* inventory.cpp */
    #include "inventory.h" // Ich brauche die Klasse, um ihre Methoden implementieren zu können
    #include <string> // Irgendwo in diesen Methoden benutze ich string
    
    /* Die Methoden */
    


  • Mach mal hinter die geschweiften Klammern des Konstruktors ein Semikolon...



  • Wozu?



  • Wenigstens damit der unregistrierte Feigling Ruhe gibt 😛



  • hallo nochmal,

    also die folgende inventory.cpp

    #include <iostream>
    #include "etc_item.h"
    
    void inventory::stats()
    {
        //etc_items :
        if (etc_items.size()==0) {cout<<"Nixdrin"<<endl;}
        else
        {
        for(unsigned int i=0;i<=etc_items.size()-1;i++)  //search for existence of the stackable item
        {
            cout<<etc_items[i].amount<<" Einheiten der ID "<<etc_items[i].etc_item_id<<endl;
        };
        }
    }
    

    in Kombination mit der folgenden inventory.h

    #ifndef INVENTORY_H
    #define INVENTORY_H
    #include <vector>
    #include "etc_item.h" // Die Datei in der Deine Klasse etc_item enthalten ist
    #include "weapon.h" // Die Datei in der Deine Klasse weapon enthalten ist
    
    class inventory
    	{
    	public:
    	//hier müssen die 3 vektoren für etc, armor und weapons rein.
    	std::vector<etc_item> etc_items;
    	//std::vector<weapon> weapons;
    	//std::vector<armor> armors;
    
    	inventory(){}; //semikolon eingefügt
    	unsigned int weight();	
    	//int add_armor (unsigned long itid);
    	int add_weapon (unsigned long itid);	
    
    	//int has_etc has_weapon has_armor (unsigned long itid, unsigned short int how_much); 
    	//int remove_etc remove_weapon remove_armor (unsigned long itid, unsigned short int how_much);
    	//exchangeorder (slotx, sloty)
    	bool isfull();
    	void stats();
    	short int add_etc (unsigned long itid, unsigned short int how_much);
    	bool has_etc(unsigned long itid, unsigned short int how_much);
    	bool remove_etc(unsigned long itid, unsigned short int how_much);
    };
    #endif
    

    bringt nun folgenden Fehler:

    --------------------Configuration: nn - Debug--------------------
    Compiling source file(s)...
    inventory.cpp
    inventory.cpp:5: error: syntax error before `::' token
    inventory.cpp:11: error: syntax error before `<=' token
    inventory.cpp:11: error: syntax error before `++' token
    nn.exe - 3 error(s), 0 warning(s)
    

    Der Compiler den ich benutze ist der vom MinGW developer Studio 2.05.

    MinGWStudioFullSetup-2.05.exe (26,633,338)
    - Files in packkage No. 1.
    - MinGW Developer Studio files
    - MinGW(C/C++ only) Compiler 3.4.2
    - Binutils 2.15.94
    - Windows32 API 3.2
    - MinGW Runtime Lirbraries 3.7
    - GNU Debugger(GDB) 6.0
    - GNU Make 3.80.0-3
    - Windows API Reference Manual

    Ich kann natürlich den kompletten Sourcecode posten, aber ich denke keiner hat Lust in seiner Freizeit an die 1000 Codezeilen durchzulesen...
    Daher hab ich versucht mich an auf die wesentlichen Stellen zu beschränken.
    😞 Also langsam beginne ich mich an den Gedanken zu gewöhnen, die Klassendefinitionen in .h Dateien auszulagern und die Methoden halt in der main.cpp zu haben.

    Vielleicht ist es nicht so wichtig, aber ich erwähne es doch mal explizit.
    Die Klassen, die ich habe bauen aufeinander auf. Sprich es gibt eine Urklasse, von der sich alles ableitet: item.h Da ist nicht viel drin, aber etc_item.h, armor.h und weapon.h sind Tochterklassen der Art "class etc_item : public item".
    Diese drei abgeleiteten benutze ich in einer neuen Klasse inventory.h ohne dass ich der inventory.h sage, dass etc_item von item abhängt. (in der etc_item.h ist natürlich die item.h inkludiert)... Vielleicht liegts ja daran?



  • In meinem Beispiel ist inventory.h in inventory.cpp eingebunden, bei Dir nicht!

    Der Compiler muss doch beim Auftauchen von inventory::stats wissen dass inventory eine Klasse ist, das kann er nur dann...


Anmelden zum Antworten