Klasse für Menüitems - Error C4430



  • Hallo,

    dies ist mein erster Beitrag in diesem Forum, ich hoffe dass ich im richtigem Bereich bin, habe keinen besseren gefunden.

    Vorgestern habe ich angefangen, C++ zu programmieren. Vorher habe ich Erfahrungen mit Delphi/Pascal und Java gemacht. Um mehr Möglichkeiten zu haben und einfach mehr zu kennen, wollte ich nun C++ lernen. Ich bin also noch ziemlicher Anfänger.

    Ich möchte gerne eine Art Snake mit Hilfe von SFML programmieren. Dazu orientiere ich mich an einem Tutorial (um die Grundlagen von SFML kennen zu lernen).

    Ich habe eine Klasse für das Hauptmenü erstellt und möchte gerne Menüpunkte hinzufügen. Dafür habe ich die Klasse MenuItem erstellt.

    #ifndef MENUITEM_HPP
    #define MENUITEM_HPP
    
    #include <string>
    #include "Game.h"
    
    using namespace std;
    
    class MenuItem
    {
    public:
    	MenuItem(string, sf::Font, int, int, int);
    	MenuItem();
    	~MenuItem();
    	void setTitle(string);
    	void setSize(int);
    	void setFont(sf::Font);
    	void setPosition(int, int);
    	void setSelectedColor(sf::Color);
    	void setDefaultColor(sf::Color);
    
    	string getTitle();
    	int getSize();
    	int getPositionX();
    	int getPositionY();
    	sf::Color getSelectedColor();
    	sf::Color getDefaultColor();
    
    	bool checkIfHovered(Game& game);
    	void draw(Game& game);
    
    private:
    	sf::Font font;
    	sf::Text text;
    	sf::Color selectedColor, defaultColor;
    };
    
    #endif
    

    Die Klasse für das Hauptmenü sieht so aus:

    #ifndef MAINMENUSTATE_HPP
    #define MAINMENUSTATE_HPP
    
    #include "MenuItem.h"
    #include "Game.h"
    
    class MainMenuState : public Gamestate
    {
    public:
    	MainMenuState();
    	~MainMenuState();
    	void HandleEvents(Game& game);
    	void Update(Game& game);
    	void Draw(Game& game);
    
    private:
    	bool bSinglePlayerSelected, bLocalMultiPlayerSelected, bQuitGameSelected;
    	sf::Font font;
    	MenuItem singlePlayer;
    	MenuItem localMultiPlayer;
    	MenuItem quitGame;
    };
    
    #endif
    

    Beim Starten bekomme ich nun für die 3 letzen Zeilen der MainMenuState Klasse jeweils die beiden Errors error C2146: syntax error : missing ';' before identifier 'singlePlayer' und error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

    Durch recherchieren habe ich bereits herausgefunden, dass bei den includes etwas falschgelaufen ist oder sich Klassen gegenseitig aufrufen. Genau verstanden habe ich es aber nicht und - egal was ich probiert habe - eine Lösung gefunden habe ich auch nicht.

    Ich würde mich sehr freuen, wenn mir jemand erklären kann, wo genau der Fehler liegt. Falls ich noch weiteren Code zeigen soll, ist das natürlich kein Problem.

    Kennt jemand von euch ein gutes C++ Tutorial, dass nicht das Programmieren von Anfang an erklärt, sondern sich auf die Unterschiede von C++ gegenüber anderen Sprachen konzentriert?

    Liebe Grüße,
    Smofe



  • sf::Front ist ein unbekannter Type. Du musst SFML includieren in der mainmenustate.h.

    #include <SFML/Graphics.hpp>
    


  • Also bei sf::Font wird kein Error ausgegeben und das Funktioniert auch auf jeden Fall; hatte vorher schon die Menüitems einzeln und nicht als eigene Klasse programmiert, da hat es auch geklappt.

    sf::Font müsste er durch den include von Game.h bereits kennen, wenn ich mich gerade nicht komplett vertue (habe das include system von c++ noch nicht vollständig durchschaut)

    Der Fehler liegt in den Zeilen:

    MenuItem singlePlayer
    MenuItem localMultiPlayer
    MenuItem quitGame
    


  • In game.h steht include menuitem.h oder menustate.h?



  • #ifndef GAME_HPP
    #define GAME_HPP
    
    #include <iostream>
    #include <memory>
    #include <SFML\Graphics.hpp>
    #include "Gamestate.h"
    #include "MainMenuState.h"
    
    class Game
    {
    public:
    	Game();
    
    	void Run();
    	bool isRunning();
    
    	bool running;
    	sf::RenderWindow window;
    
    private:
    	std::unique_ptr<Gamestate> CurrentState;
    };
    
    #endif
    


  • Das geht so nicht.
    #include ist reine Textersetzung.
    Du kannst nicht in Datei A Datei B includen und in Datei B Datei A.
    So sieht das aus, nachdem der Präprozessor die #include und Includeguard für MenuItem.h abgearbeitet hat (System-Header entfernt):

    class MainMenuState : public Gamestate
    {
    public:
        MainMenuState();
        ~MainMenuState();
        void HandleEvents(Game& game);
        void Update(Game& game);
        void Draw(Game& game);
    
    private:
        bool bSinglePlayerSelected, bLocalMultiPlayerSelected, bQuitGameSelected;
        sf::Font font;
        MenuItem singlePlayer; // eh, was zur Hölle ist MenuItem ???
        MenuItem localMultiPlayer; // Header wurde nicht eingefügt, weil man sich gerade in MenuItem.h befindet
        MenuItem quitGame; // d.h. der Includeguard blockt das
    };
    
    class Game
    {
    public:
        Game();
    
        void Run();
        bool isRunning();
    
        bool running;
        sf::RenderWindow window;
    
    private:
        std::unique_ptr<Gamestate> CurrentState;
    };
    
    using namespace std;
    
    class MenuItem
    {
    public:
        MenuItem(string, sf::Font, int, int, int);
        MenuItem();
        ~MenuItem();
        void setTitle(string);
        void setSize(int);
        void setFont(sf::Font);
        void setPosition(int, int);
        void setSelectedColor(sf::Color);
        void setDefaultColor(sf::Color);
    
        string getTitle();
        int getSize();
        int getPositionX();
        int getPositionY();
        sf::Color getSelectedColor();
        sf::Color getDefaultColor();
    
        bool checkIfHovered(Game& game);
        void draw(Game& game);
    
    private:
        sf::Font font;
        sf::Text text;
        sf::Color selectedColor, defaultColor;
    };
    


  • Gut, schonmal danke. Das der Fehler in die Richtung geht hatte ich ja bereits vermutet.

    Wie könnte man das denn alternativ machen?

    Mein Problem ist eben, dass ich zwar das Programmieren an sich halbwegs verstehe, aber mich nicht mit C++ konkret auskennt. Kannst du da ein Tutorial empfehlen?



  • Du brauchst in Game.h doch nur

    #include <memory>
    #include <SFML/Graphics.hpp>
    #include "Gamestate.h"



  • Nur das includieren, was der Header (!) zum Übersetzen braucht.
    Im einzelnen:
    MainMenuState.h ist ok so, forward declarations würden zu weit führen.
    MenuItem.h braucht kein Game.h, es braucht nur den Graphics Header von SFML (BTW: Kein using namespace in Headerdateien, böse!)
    Game.h braucht kein iostream und kein MainMenuState.h.



  • In .cpp-Dateien kann man praktisch inkludieren, was man will, solange man .cpp-Dateien niemals in andere .cpp-Dateien (oder gar in .h-Dateien) inkludiert. So entstehen keine zyklischen includes.

    Kniffliger ist die Sache mit .h-Dateien, denn die sollen (und werden) in andere inkludiert.

    Hier muß man das beachten, was Nathan schreibt - indem man sich vorstellt, wie der Textstrom aus dem Präprozessor herauskommt und was demnach der Compiler zu sehen bekommt.
    (- lernen zu denken wie ein Compiler ist ja sowieso keine schlechte Idee, wenn man C++ programmiert)

    ich vermeide zyklische Includes durch das Prinzip "nach einer Seite includieren, nach der anderen Seite deklarieren" - d.h. wenn A von B abhängt und B von A, dann schreibe ich in A.h "#include B.h" und in B.h
    "class A;"



  • Nathan schrieb:

    Nur das includieren, was der Header (!) zum Übersetzen braucht.
    Im einzelnen:
    MainMenuState.h ist ok so, forward declarations würden zu weit führen.
    MenuItem.h braucht kein Game.h, es braucht nur den Graphics Header von SFML (BTW: Kein using namespace in Headerdateien, böse!)
    Game.h braucht kein iostream und kein MainMenuState.h.

    Ich habe in MenuItem.h doch zwei Funktionen, die einen Parameter vom Typ Game haben, also muss ich doch Game includen?

    bool checkIfHovered(Game& game);
    void draw(Game& game);
    

    Ohne das using namespace schmeißt mir Visual Studio aber Errors ohne Ende bei den strings entgegen, wie kann ich das denn umgehen?

    Danke für die Hilfe! 🙂



  • Sorry hab die zwei nicht gesehen. Dann behalt das include. Oder informiere dich über forward declarations, dann brauchst du das auch nicht.
    Zum Namespace: Dann machst du dasselbe wie mit dem Namespace von SFML, der heißt sf und die Elemente daraus prefixest du mit sf::, bei der Standardibliothek heißt der Namespace std und die Elemente prefixest du dann entsprechend mit std::.



  • Super, vielen Dank! Die ursprünglichen Fehler sind damit behoben.

    Also kann ich mir grundsätzlich merken:

    • So wenige includes in headern wie möglich
    • Bei .cpp Datein sind die includes nicht so entscheidend (dürfen auch mal zuviele sein)
    • Kein using namespace in headern

    2 Fehler habe ich aber noch, die haben allerdings nicht umbedingt etwas mit den Ursprungsfehlern zu tun:

    Error 2 error LNK2019: unresolved external symbol "public: __thiscall MenuItem::~MenuItem(void)" (??1MenuItem@@QAE@XZ) referenced in function __unwindfunclet$??0MainMenuState@@QAE@XZ$4 C:\Users\Michel\Documents\Visual Studio 2013\Projects\SFML Test\SFML Test\MainMenuState.obj Action Snake

    Error 3 error LNK1120: 1 unresolved externals C:\Users\Michel\Documents\Visual Studio 2013\Projects\SFML Test\Debug\Action Snake.exe Action Snake

    Die .cpp Datei der MainMenuState Klasse sieht folgendermaßen aus:

    #include "MainMenuState.h"
    #include <iostream>
    
    MainMenuState::MainMenuState()
    :singlePlayer("Singleplayer", font, 45, 500.f, 350.f),
    localMultiPlayer("Local Multiplayer", font, 45, 500.f, 400.f),
    quitGame("Quit Game", font, 45, 500.f, 450.f)
    {
    	//Load font and menu item text
    	font.loadFromFile("assets\\fnt\\calibri.ttf");	
    
    }
    
    void MainMenuState::HandleEvents(Game& game)
    {
    	//Handle all events
    	sf::Event event;
    
    	while (game.window.pollEvent(event))
    	{
    		if (event.type == sf::Event::Closed)
    		{
    			game.window.close();
    			game.running = false;
    		}
    	}
    }
    
    void MainMenuState::Update(Game& game)
    {
    	//Check if mouse is over menu item
    	bSinglePlayerSelected = singlePlayer.checkIfHovered(game);
    	bLocalMultiPlayerSelected = localMultiPlayer.checkIfHovered(game);
    	bQuitGameSelected = quitGame.checkIfHovered(game);
    
    }
    
    void MainMenuState::Draw(Game& game)
    {
    	//Renders the scene
    	singlePlayer.draw(game);
    	localMultiPlayer.draw(game);
    	quitGame.draw(game);
    
    }
    
    MainMenuState::~MainMenuState()
    {
    	//Destruktor of the class
    	std::cout << "Menudestruktor wurde aufgerufen!" << std::endl;
    }
    


  • Smofe schrieb:

    • So wenige includes in headern wie möglich
    • Bei .cpp Datein sind die includes nicht so entscheidend (dürfen auch mal zuviele sein)
    • Kein using namespace in headern

    Ja, das ist soweit richtig. Zu viele includes in cpp sind natürlich auch nicht so schön, schaden aber auch nicht sonderlich (insbesondere nicht bei Projekten deiner Größe).

    2 Fehler habe ich aber noch, die haben allerdings nicht umbedingt etwas mit den Ursprungsfehlern zu tun:

    Error 2 error LNK2019: unresolved external symbol "public: __thiscall MenuItem::~MenuItem(void)" (??1MenuItem@@QAE@XZ) referenced in function __unwindfunclet$??0MainMenuState@@QAE@XZ$4 C:\Users\Michel\Documents\Visual Studio 2013\Projects\SFML Test\SFML Test\MainMenuState.obj Action Snake

    Error 3 error LNK1120: 1 unresolved externals C:\Users\Michel\Documents\Visual Studio 2013\Projects\SFML Test\Debug\Action Snake.exe Action Snake

    Irgendwas mit "unresolved external symbol" oder "undefined reference" heißt immer: du hast eine Funktion deklariert, aber nirgends definiert. In diesem Fall ist das der Destruktor der MenuItem Klasse. Da du in den Destruktoren aber eh nur (unnötige) Logausgaben machst, kannst du die auch weglassen.

    Nur so aus Neugier: Kannst du mal das Tutorial verlinken, das scheint richtig gut zu sein, dass es dir std::unique_ptr beibringst.

    Zum weiteren Lernen von C++: Da du scheinbar schon Erfahrungen im Programmieren hast, ist The C++ Programming Language vom Erfinder der Sprache selber oft empfohlen werden:
    The C++ Programming Language | ISBN: 9780321563842
    Ich selber kenne es nicht, aber es soll die grundlegenden Grundlagen nicht detailliert behandlen, sondern eher die Sprache C++ vorstellen.



  • Schau dir mal nebenbei die SFML Engine von den Entwiklern an. Der Code ist aus dem Buch: http://www.amazon.de/SFML-Game-Development-Jan-Haller/dp/1849696845

    Code: https://github.com/LaurentGomila/SFML-Game-Development-Book


  • Mod

    In deinem Eröffnungsbeitrag wird in Zeile 14 ein selbstgeschriebener Destruktor deklariert. Den implementierst du aber anscheinend nirgendwo (oder an einem Ort, den der Linker nicht finden kann). Brauchst du überhaupt einen selbstgeschriebenen Destruktor? Falls ja, wäre deine Klasse höchstwahrscheinlich unvollständig, da du dann die Regel der großen Drei verletzen würdest. Das sieht mir doch eher danach aus, als würde der compilergenerierte Destruktor vollständig ausreichen (wie es normal ist) und du kannst dir die Deklaration einfach komplett sparen.

    Das gleiche gilt höchstwahrscheinlich auch für den selbstgeschriebenen Standardkonstruktor in der Zeile davor, bloß rufst du diesen anscheinend sowieso nirgendwo auf.

    Wenn dir irgendein Lehrbuch beigebracht haben sollte, dass du immer eigene Destruktoren und Standardkonstruktoren schreiben solltest, selbst wenn diese dann leer sind: Ein Fall fürs Altpapier.



  • Ja, es lag tatsächlich an dem Destruktor.

    Vielen Dank! Ihr habt mir wirklich sehr geholfen. Ich werde mir die beiden Bücher mal angucken, vielleicht befindet sich ja auch eins davon in der Bücherei vorort 🙂

    Ein Lehrbuch hat mir hier nichts beigebracht - das war ja auch das Problem. Jetzt klappt jedenfalls alles! 🙂

    Einen schönen Abend noch.

    Edit: Die Konstruktoren sind alle mit Code befüllt - nur der Destruktor war leer 🙂


Anmelden zum Antworten