SFML OOP Problem



  • Hi
    ich habe folgende Klasse geschrieben, um den Grundstein für meine ersten Spiele zu legen:

    Ball.hpp

    #pragma once
    
    #include <SFML\Graphics.hpp>
    #include <string>
    
    class CBall : public sf::RenderWindow {
    public:
    	CBall(sf::VideoMode vmMode, const std::string& strTitle);
    	~CBall();
    
    private:
    	sf::Event ev;
    
    	void onExecute();
    	void onEvent();
    };
    

    Ball.cpp

    #include "Ball.hpp"
    
    CBall::CBall(sf::VideoMode vmMode, const std::string& strTitle) {
    	sf::RenderWindow(vmMode, strTitle);
    
    	onExecute();
    }
    
    CBall::~CBall(void) {
    }
    
    void CBall::onExecute() {
    	while(this->IsOpened()) {
    
    		onEvent();
    
    		this->Clear();
    		this->Display();
    	}
    }
    
    void CBall::onEvent() {
    	while(this->GetEvent(ev)) {
    		if(ev.Type == sf::Event::Closed) {
    			this->Close();
    		}
    	}
    }
    

    Main.cpp

    #include <SFML\Window\VideoMode.hpp>
    #include <string>
    
    #include "Ball.hpp"
    
    int main() {
    	CBall ball(sf::VideoMode(640, 480, 32), "SFML Ball");
    
    	return 0;
    }
    

    Ich denke, dass mein Problem wieder ziemlich simpel ist.
    Aus der onExecute-Methode springt das Programm direkt raus, weil das Fenster anscheinend noch nicht offen ist. Ich habe das Fenster aber ganz kurz gesehen. - Was muss ich anders machen?

    LG
    Cooter

    PS: Ich nehme auch gerne weitere Tipps an 😋 ich freu mich drauf.



  • CBall::CBall(sf::VideoMode vmMode, const std::string& strTitle) { 
        sf::RenderWindow(vmMode, strTitle);
    

    Das da is kein Konstruktoraufruf sondern erzeugt ein temporäres Objekt vom Typ sf::RenderWindow. Was du willst nennt sich Initialisierungsliste.



  • #include <SFML\Graphics.hpp>
    

    Ich würde als Pfad-Separator "/" nehmen.

    class CBall : public sf::RenderWindow
    

    Ein Ball ist ein Fenster? Wohl eher nicht, also ist Vererbung nicht der richtige Weg. Überhaupt ist sf::RenderWindow nicht zur Vererbung konzipiert. Nimm ein sf::RenderWindow -Objekt als Member und leite die nötigen Funktionen weiter.

    CBall(sf::VideoMode vmMode, const std::string& strTitle);
    

    Wenn du schon den String als Referenz übergibst, kannst du das eigentlich auch mit dem sf::VideoMode tun.

    sf::Event ev;
    

    Gibt es einen Grund, wieso der Event in der Klasse und nicht lokal in der Funktion deklariert ist? Denn er gehört nicht zum Objekt-Status, also deklariere ihn doch erst in onEvent() .

    CBall::~CBall(void) {
    }
    

    Einen leeren nicht-virtuellen Destruktor, der rein gar nichts tut, kannst du geradeso gut weglassen. Er wird vom Compiler generiert.

    Ausserdem ist void in C++ bei Parameterlisten eher unüblich. In C musste man es schreiben, in C++ lässt man die Parameterliste normalerweise leer.

    while(this->IsOpened()) {
    
    		onEvent();
    
    		this->Clear();
    		this->Display();
    

    Warum manchmal this-> und manchmal nicht? Sei hier lieber konsequent (am einfachsten ohne this-> ).



  • Noch ein paar Anmerkungen zum Präfix "C" bei Klassen wie CBall :

    • Das Klassenpräfix "C" stand urprünglich als Bibliothekspräfix für die MFC, also wie ein Namensraum. Ein paar Leute sind aber auf die Idee gekommen, das selbst auch anzuwenden, obwohl in C++ eigentlich kein wirklicher Vorteil dafür spricht.
    • Dass es sich um eine Klasse handelt, erfährst du bei modernen IDEs beim Darüberfahren mit der Maus. Aber oft weiss man das schon oder braucht es gar nicht zu wissen. Und wenn man es tatsächlich herausfinden muss, kann man gleich in die Dokumentation schauen, weil man sehrwahrscheinlich weitere Informationen benötigt.
    • Meist gibt es keinen Grund, Klassen künstlich von einfachen Typen abzugrenzen. Im Vordergrund stehen die Konzepte und die Semantik des Typen und nicht dessen exakte Deklarationssyntax. Teilweise ist die Behandlung sogar genau gleich (z.B. eine Klasse Double mit überladenen Operatoren und der eingebaute Typ double ), dann macht eine Abgrenzung erst recht keinen Sinn. C++0x geht mit seiner Typinferenz ( auto und decltype ) ebenfalls in diese Richtung.
    • Wenn du eine Sonderbehandlung für class einrichtest, musst du im Prinzip auch eine für struct und enum erstellen (oder gemeinsam verwenden), wenn nicht sogar für mehr Typen.
    • Das "C"-Präfix zwingt dich zur Inkonsistenz. Du kannst niemals alle Klassentypen so kennzeichnen, wenn dein Projekt etwas grösser wird. Denn sobald du Abstraktionsmechanismen wie typedef oder template verwendest, musst du darauf verzichten. Aber das "C" nur manchmal hinzuschreiben, entfernt sich von der Idee, Klassentypen schnell im Code zu erkennen, und reduziert die tatsächlichen Vorteile dieser Namenskonvention.


  • Nexus schrieb:

    Das Klassenpräfix "C" stand urprünglich als Bibliothekspräfix für die MFC, also wie ein Namensraum. Ein paar Leute sind aber auf die Idee gekommen, das selbst auch anzuwenden, obwohl in C++ eigentlich kein wirklicher Vorteil dafür spricht.

    Genau. Das lustige an der Sache ist also dass all die Leute die diese Präfixe verwenden damit genau gegen den eigentlichen Sinn derselben handeln. Auch wenn viele sich was anderes einbilden kann man da also eigentlich nur von einer leider sehr verbreiteten Unsitte reden.



  • Vielen Dank für die ganzne Anworten.

    Zum Namen meiner Klasse kann ich sagen, dass der Name ein bisschen verunglückt ist bzw. falsch gewählt. Ich wollte in dem gesamten Projekt nur einen Ball im Fenster "hin und her titschen" lassen. Deswegen der Name.
    Das void mache ich generell auch weg - wurde halt nur von Visual C++ automatisch erstellt - habe ich übersehen 😛

    Ok - ich werde nun folgendes machen:

    1. RenderWindow als Member deklarieren -> auf Vererbung verzichten.
    2. Das mit dem C-Präfix sehe ich ein - ist logisch. Lasse ich einfach weg 😉
    3. Anschließend überarbeite ich meinen Code, damit er einheitlich ist.

    Danke sehr. Habt mir sehr geholfen.


  • Mod

    guten code sollte man lesen koennen ohne dass man auf irgendwelche tools setzt, das merkt man besonders wenn man anfaengt nicht fuer sich alleine zu arbeiten. jeder stellt sich sein 'highlighting' usw. seinen wuenschen entsprechend ein und wenn man an einen anderen schreibtisch geht, mit anderen settings oder sogar anderen editoren, sollte man dennoch wissen worum es im code geht ohne die maus ueber jede variable usw zu bewegen. auch diff tools highlighten meist anders, nicht zwingend im code context, sondern eher nach arbeits context (sprich, sinnvoll unterschiede abbilden).
    entsprechend ist es gute sitte, und von vielen code guidelines (in firmen) vorgeschrieben den context im namen abzubilden, damit jeder auf den ersten blick (nicht aufs erste highlighting) den code verstehen kann und auch nicht z.b. durch deklarieren von lokalen variablen, member variablen ueberdeckt. wie gesagt, mit fremden code arbeiten und es wird einem passieren.

    falls man darueber weiter diskutieren will, leg ich gerne einen neuen thread an und verschiebe es in ein passendes forum.



  • Ich würde gerne wissen, was ihr als Profis jetzt für richtig erachtet, da ich meinen Code später nicht nur für mich behalten will, sondern mich gerne austauschen würde. Ich denke, dass ein schnelles Verständniss des Codes sehr wichtig ist.

    LG
    Cooter


  • Mod

    das ist recht einfach, du musst dich mit den leuten absprechen bzw einigen wie ihr code schreibt. ich schreibe meine auch so wie das team es will und passe mich an die teams an. manche schreiben

    typedef int tdFoo;
    template<..>
    class TBar
    {
    };
    

    andere

    typedef int TFoo;
    template<...>
    class CBar
    {
    };
    

    welches ist richtig?
    keines!
    es muss einfach nur einheitlich sein, damit alle es verstehen.

    und der syntax ist das kleines uebel, man muss sich auf libraries, pattern, programmiersprache usw. einigen und da hat jeder seine vorlieben und muss dann einfach fuers team arbeiten. natuerlich ist die eigene ansicht 'die richtige' weil man ja sonst nicht so arbeiten wuerde wie man es macht, jedoch hat sicher jeder seine argumente und ...

    um mal jemanden weisen zu zittieren
    "The right tool for the right job" (Scotty)



  • Wobei Code auch verständlich sein kann, ohne dass man die diese Präfixe benutzt. Konventionen wie Typen/Funktionen gross, Variablen klein, Membervariablen mit "m" etc. und vor allem gute Bezeichner machen schon sehr viel aus. Es gibt ja genügend Bibliotheken, die zeigen, dass es gut ohne geht. Libraries, die "C" etc. als Präfix nehmen, fällt mir neben den MFC hingegen nur gerade eine ein – Irrlicht. Und da wird das nicht 100% konsistent gemacht, selbst wo es möglich wäre.

    Doch wie bereits angetönt: Wenn ihr eine Sitzung habt und euch einen bestimmten Typen anschaut, bringt euch ein Präfix auch nicht allzu viel. Ihr müsst wissen, wofür der Typ semantisch steht, und dann wisst ihr normalerweise auch, was für ein Typ es ist (sofern dies überhaupt relevant sein sollte). Natürlich kannst du z.B. IParticleEmitter und CParticleEmitter auseinander halten, aber sonst hiesse es halt AbstractParticleEmitter und ParticleEmitter oder so.

    Und das Maus-Darüberfahren ist ja nicht das einzige Argument gegen Klassenpräfixe. Klar können Präfixe ab und zu praktisch sein, aber sie bringen eben auch viele Inkonsistenzen und Probleme mit sich. Vor allem versprechen sie mehr, als sie halten können – nämlich Typen einheitlich zu kategorisieren.


Anmelden zum Antworten