Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

11

26.06.2016, 13:12

Also ich habe mal eben grob drüber geschaut und folgendes ist mir aufgefallen.
  • Größere Objekte sollten als Pointer/Referenz übergeben werden, falls nicht aus irgendeinem Grund tatsächlich eine Kopie gebraucht wird.
    Z.B. hast du ganz oft in deinem Code

    C-/C++-Quelltext

    1
    
    const string filename
    anstatt

    C-/C++-Quelltext

    1
    
    const string& filename

    Sowas hast du u.A. in Button, CMenu, CSoundwork, CSprite und CTournament (kann auch sein, dass ich welche übersehen habe):

    In Fight::Init übergibst du Player und Enemy ebenfalls nicht als Pointer/Referenz.

    C-/C++-Quelltext

    1
    
    void Init(CMonster Player, CMonster Enemy, CSprite *pBackground, int FoodReward);

    Habe es jetzt nicht weiter verfolgt, ob du da absichtlich eine Kopie brauchst, aber auf den ersten Blick sieht es falsch aus.

    Gleiches gilt auch für Return Werte. In der Button Klasse hast du z.B.

    C-/C++-Quelltext

    1
    
    SDL_Rect GetRect()
    anstatt

    C-/C++-Quelltext

    1
    
    const SDL_Rect& GetRect() const
    .

  • Objekte sollten, soweit möglich, auf dem Stack leben.

    C-/C++-Quelltext

    1
    2
    3
    4
    5
    
    CButton::CButton()
    {
        m_pSpriteButtonActive = new CSprite;
        m_pSpriteButtonPassive = new CSprite;
    }

    Sowas macht man in Java, C#, ..., aber nicht in C/C++.
    Noch schlimmer ist es, dass die mit new angelegten Objekte oft auch nicht wieder freigegeben werden.

    Gefunden habe ich sowas u.A. in Button, Fight, Game, MonsterInventory und CSettings

  • Implementierungen gehören in den Source file, nicht in den Header.
    Das kam z.B. in Button.hpp und MainFunctions.hpp vor.

  • Methoden, die das Objekt nicht verändern sollten const sein, z.B. in Button:

    C-/C++-Quelltext

    1
    
    SDL_Rect GetRect()
    sollte

    C-/C++-Quelltext

    1
    
    SDL_Rect GetRect() const
    sein.

  • In Main.cpp:

    C-/C++-Quelltext

    1
    2
    3
    4
    5
    6
    
    if (g_pFramework->Init(800, 600, 16, false) == false)
        return (0);
    
    // Soundwork initialisieren
    if (g_pSoundwork->Init(44100, AUDIO_S16SYS, 2, 4096) == false)
        return (0);

    Es wäre hier sinnvoll 1 oder zumindest etwas anderes als 0 als Error code zurückzugeben.

  • Initialisierungslisten verwenden z.B. (Code gekürzt):

    C-/C++-Quelltext

    1
    2
    3
    4
    5
    
    CSprite::CSprite()
    {
        m_pImage = NULL;
        m_pTexture = NULL;
    }
    sollte

    C-/C++-Quelltext

    1
    2
    
    CSprite::CSprite() : m_pImage(NULL), m_pTexture(NULL)
    {}
    sein.
    Grund dafür ist, dass der Compiler für alle Variablen, die nicht in der Initialisierungsliste vorkommen, den Standard Konstruktor aufruft. Indem man gleich die richtige Werte angibt, kann man verhindern, dass die Variablen erst mit unsinnigend Standardwerten gefüllt werden.
    Für builtin types, wie int, short, ... dürfte es allerdings keinen großen Unterschied machen, da sie afaik keinen Standardwert bekommen, sondern dann einfach einen undefinierten Wert haben.
Das wären so die Dinge, die mir auf den ersten Blick aufgefallen sind.

TalvinEx

Frischling

  • »TalvinEx« ist der Autor dieses Themas

Beiträge: 28

Wohnort: NRW

  • Private Nachricht senden

12

26.06.2016, 13:42

Okay, danke schonmal für deine Rückmeldung! :thumbup:

Werde mir die angesprochenen Sachen auf jeden Fall mal anschauen und verbessern! Initialisierungslisten z.B kannte ich überhaupt nicht, hört sich eigentlich ziemlich sinnvoll an.
Sry, dass ich so blöd frage, aber was bringt es eine Funktion const zu machen? Also wo du hinter die Funktionsdeklaration einfach const geschrieben hast? Hab das nämlich noch nie gesehen ?(

Und String gilt tatsächlich als größeres Objekt? Also sollte man quasi alles außer den Standard-Datentypen per Referenz oder Zeiger übergeben?

(Edit): Objekte sollten soweit möglich auf dem Stack leben bedeutet, dass ich die Sprites nicht als Zeiger erzeuge, also in der Klassendeklaration von CButton bspw.:

C-/C++-Quelltext

1
2
CSprite m_SpriteButtonActive;
CSprite m_SpriteButtonPassive;

anstelle von

C-/C++-Quelltext

1
2
CSprite *m_pSpriteButtonActive;
CSprite *m_pSpriteButtonPassive;

ist das richtig?

(Edit_2): Also ich hab mir das jetzt nochmal in Heiko Kalistas Buch "C++ für Spieleprogrammierer" angeschaut, weil bei mir eine Zugriffsverletzung im Konstruktor von Sprite kommt, wenn der zweite Button ins Menü geladen wird. Er erzeugt die Sprites im Projekt am Ende des Buches auf die gleiche Weise und ich denke mal, dass ich das daher habe..
Also was genau ist daran jetzt verkehrt, weil ich gerade versucht bin das einfach so zu lassen, denn es funktioniert ja scheinbar nicht so wie ich es oben beschrieben habe :/ :?:
"Gewinner haben keine Angst vor dem Verlieren, nur Verlierer"
(~ein weiser Mann)

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »TalvinEx« (26.06.2016, 16:20)


13

27.06.2016, 21:50

Sry, dass ich so blöd frage, aber was bringt es eine Funktion const zu machen? Also wo du hinter die Funktionsdeklaration einfach const geschrieben hast? Hab das nämlich noch nie gesehen ?(
Das bedeutet, dass die Funktion das Objekt nicht verändern darf. Das ist sinnvoll, wenn du ein const Objekt hast dessen Funktionen du aufrufen willst. Wenn du versuchst eine nicht-const Funktion von einem const Objekt aufzurufen bekommst du einen Compilerfehler.
Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
class A
{
public:
    int returnTruth() { return 42; }
};

bool checkTruth(const A& a)
{
    return a.returnTruth() == 42; // Fehler, da a const ist, aber returnTruth nicht.
}

Für eine ausführlichere Erklärung zum Thema const-correctness kannst du dir mal das hier durchlesen. Hier ist der Abschnitt zu const Methoden.

Und String gilt tatsächlich als größeres Objekt? Also sollte man quasi alles außer den Standard-Datentypen per Referenz oder Zeiger übergeben?
Ja, so ziemlich, es sei denn du hast einen guten Grund eine Kopie zu erstellen, z.B. wenn du eine Variable im Scope deiner Funktion verändern willst.

Objekte sollten soweit möglich auf dem Stack leben bedeutet, dass ich die Sprites nicht als Zeiger erzeuge [...] ist das richtig?
Richtig. Du darfst dann natürlich auch nicht mehr new verwenden, da das Objekt ja bereits erzeugt wurde.

Also was genau ist daran jetzt verkehrt, weil ich gerade versucht bin das einfach so zu lassen, denn es funktioniert ja scheinbar nicht so wie ich es oben beschrieben habe
Weil Objekte auf dem Stack automatisch aufgeräumt werden. Dadurch kann es nicht passieren, dass man versehentlich irgendwo vergisst wieder was freizugeben. Außerdem ist der Stack schneller. In diesem Stackoverflow post wird das ganze nochmal etwas genauer erklärt.
Wenn du Segfaults bekommst, musst du halt die entsprechenden Stellen suchen (evtl. mit Debugger) und fixen. Da ich deinen Code nicht kenne, kann ich dir da auch nicht viel weiterhelfen.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Malloc« (28.06.2016, 10:55)


14

27.06.2016, 22:23

Wurde schon erwähnt, Konstruktoren diesen Init-Methoden und nullptr NULL vorzuziehen?

TalvinEx

Frischling

  • »TalvinEx« ist der Autor dieses Themas

Beiträge: 28

Wohnort: NRW

  • Private Nachricht senden

15

27.06.2016, 22:44

Wurde schon erwähnt, Konstruktoren diesen Init-Methoden und nullptr NULL vorzuziehen?


Nein, danke
Warum ist das so (vor allem nullptr, wegen init muss ich nochmal gucken)?
"Gewinner haben keine Angst vor dem Verlieren, nur Verlierer"
(~ein weiser Mann)

TalvinEx

Frischling

  • »TalvinEx« ist der Autor dieses Themas

Beiträge: 28

Wohnort: NRW

  • Private Nachricht senden

16

27.06.2016, 22:46

@Malloc

Danke, werde mir die Sachen mal angucken :thumbsup:
"Gewinner haben keine Angst vor dem Verlieren, nur Verlierer"
(~ein weiser Mann)

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

17

27.06.2016, 23:07

Wurde schon erwähnt, Konstruktoren diesen Init-Methoden und nullptr NULL vorzuziehen?


Nein, danke
Warum ist das so (vor allem nullptr, wegen init muss ich nochmal gucken)?

RAII - Resource Acquisition Is Initialization. Kurz gesagt: Nachdem du den Konstruktur aufgerufen hast, muss das gelieferte Objekt in vollem Umfang funktionsfähig sein.
https://de.wikipedia.org/wiki/Ressourcen…Initialisierung
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

18

28.06.2016, 13:21

Moinsen,

die Grafiken sind ja echt witzig :crazy:

Du solltest deinem Game einen Serializer und einen ConfigHandler schenken. Diese ganze Datei laden/speichern Arie kann sich ja keiner anschauen und Warten erst Recht nicht.


Das wars erst mal.

Grüße

Werbeanzeige