2 Elemente in Pointer-Array behalten nicht ihren Wert



  • Hat irgendjemand eine Idee, woran das liegen könnte?



  • kann es eventuell sein, dass der Destructor von WorldProvider aufegrufen wird bevor getPipeBounds ausgeführt wird?

    Ansonsten könntest du mal zeigen wie du den WorldProvider in deinem restlichen code verwendest?



  • Füge mal

    private:
      WorldProvider(WorldProvider const&);
      WorldProvider& operator=(WorldProvider const&);
    

    in der Klassendefinition hinzu, ohne diese Dinger zu definieren. Lässt sich das Programm dann noch kompilieren und linken?



  • Du könntest im Debugger einen watchpoint setzen, dann hält das Programm an, wenn sich der Wert des Pointers ändert. Ich kann dir allerdings nicht sagen, wie das in Eclipse geht, zumal du weder Betriebssystem (WinApi = Windows?) noch Compiler und Debugger genannt hast.



  • Betriebssystem: Windows 7 (64 bit)
    IDE: Eclipse Indigo (für C++)
    Compiler: MinGW
    Debugger: Keine Ahnung, ist da wohl dabei, bei Eclipse lässt sich einfach auf "Debug" klicken, also nehme ich an, dass das Teil von MinGW ist.

    @firefly
    In einer anderen Klasse (die die Game-Ticks übernimmt), wird zuerst ein (privater) Pointer auf WorldProvider deklariert:

    WorldProvider* world;
    

    Wenn das Game gestartet wird (der User drückt Enter), wird die Methode GameTicker::initGame() aufgerufen, die folgendes macht:

    void GameTicker::initGame() {
    	if(phase == PHASE_GAME)
    		return;
    	phase = PHASE_GAME;
    	RECT winRect;
    	GetWindowRect(hWnd, &winRect);
    	world = new WorldProvider(winRect.right - winRect.left,	winRect.bottom - winRect.top);
    }
    

    Im Dekonstruktor von GameTicker wird einfach "delete world" aufgerufen.

    @krümelkacker
    Nein, beim Compilen kommt folgender Fehler:

    ..\src\/WorldProvider.h:8:2: error: 'WorldProvider::WorldProvider(const WorldProvider&)' is private
    ..\src\GameTicker.cpp:59:10: error: within this context
    

    GameTicker.cpp Linie 59 ist der getter für die Welt und dereferenziert die Welt:

    WorldProvider GameTicker::getWorld() {
    	return *world;
    }
    

    (ist das eigentlich eine gute Idee? Anscheinend wird der Copy-Konstruktor aufgerufen?)



  • Wenn ich den Copy-Konstruktor public mache (was er meines Wissens doch auch sein sollte, oder?), verschwindet der erste Fehler, dann meckert er nur noch beim Dereferenzieren des Pointers:

    [...]/GameTicker.cpp:59: undefined reference to `WorldProvider::WorldProvider(WorldProvider const&)'
    

    (selbe Linie wie gerade eben, wo "*world" aufgerufen wird)

    Danke auf jeden Fall schon mal für eure Hilfe!

    ....
    Ich glaube, ich hab das Problem gelöst! Ich hab jetzt die Methode getWorld() dementsprechend umgeändert, dass sie nur den Pointer zurückgibt, und nicht die Welt selbst. (WorldProvider* als Rückgabetyp)
    Jetzt scheint alles zu funktionieren, ich hab nur keine Ahnung warum 😕

    Könnte mir das jemand erklären? 🙄


  • Mod

    Das worauf krümelkacker dich aufmerksam machen wollte ist, dass du die Regel der großen Drei verletzt haben könntest. Das war kein Vorschlag, wie man das Problem behebt, sondern ein Test, ob dies das Problem ist. Und der Test hat voll angesprungen. Du machst da eine flache Kopie von deinem Pointer und das geht natürlich von der Programmlogik her schief.

    Abhilfe: Die naive Lösung wäre natürlich, keine flache Kopie zu machen. Aber das eigentliche Problem ist, warum du überhaupt ein dynamisches Feld selber verwaltest. new[] und delete[] kommen wirklich nie in C++ vor (Eine einzige Stelle (eigener Allokator) fällt mir ein, aber das ist sehr exotisch). new[] und delete[] sind die deutlichsten Anzeichen von allen, dass man einen std::vector möchte.



  • Bone008 schrieb:

    @krümelkacker
    Nein, beim Compilen kommt folgender Fehler:

    ..\src\/WorldProvider.h:8:2: error: 'WorldProvider::WorldProvider(const WorldProvider&)' is private
    ..\src\GameTicker.cpp:59:10: error: within this context
    

    GameTicker.cpp Linie 59 ist der getter für die Welt und dereferenziert die Welt:

    WorldProvider GameTicker::getWorld() {
    	return *world;
    }
    

    Da haben wir das problem schon, getWorld liefert laut methoden signatur eine kopie des Objektes WorldProvider zurück. Ist das von dir gewollt?
    Da du keinen Kopier bzw. Zuweisungs Operator für die Klasse erstellt hast, erstellt der Kompiler diese. Und in diesen generierten Versionen werden die einzelnen Member kopiert (bei deinem array nur der pointer wert und nicht das Array als solches).



  • Eine Kopie zu erstellen war von mir keineswegs gewollt.
    Ich komme aus der Java-Programmierung, und hab nicht angenommen, dass das Zurückgeben eines Wertes eine Kopie erstellt (einzig bei Parametern habe ich den Unterschied von call-by-value und call-by-reference gelernt).

    new[] und delete[] scheint ja nicht das Problem gewesen zu sein, aber ich sollte wohl mir mal den std::vector ansehen.

    Danke für eure Hilfe, das Problem ist gelöst 🙂


  • Mod

    Bone008 schrieb:

    new[] und delete[] scheint ja nicht das Problem gewesen zu sein, aber ich sollte wohl mir mal den std::vector ansehen.

    Wie man's nimmt. Es war ein direkter Folgefehler, weil du mit rohen Zeigern rumhantierst. Gerade wenn du von Java kommst, solltest du in C++ vorsichtig sein mit Java-Techniken. Als Java-Programmierer solltest du zum Beispiel auch normales new und delete und alle Zeiger als Alarmsignal ansehen, dass du gerade Java in C++ machen möchtest (was keine gute Idee ist).



  • SeppJ schrieb:

    Als Java-Programmierer solltest du zum Beispiel auch normales new und delete und alle Zeiger als Alarmsignal ansehen, dass du gerade Java in C++ machen möchtest (was keine gute Idee ist).

    Ich hatte keine andere Lösung gefunden, um world nicht schon im Konstruktor zu initialisieren, als einen Pointer (und damit new/delete) zu verwenden.

    Die Welt soll ja erst erstellt werden, wenn initGame() aufgerufen wird, allerdings muss sie auch außerhalb von initGame verfügbar sein (kann also keine statische Variable sein). Wie funktioniert das sonst außer mit new?


  • Mod

    Na, mit einem vector als Klassenmember.

    Wobei es etwas ungewöhnlich ist, zu einem Objekt nochmal eine weitere Initialisierungsmethode anzubieten. Sollte das nicht der Konstruktor machen?



  • Das mit dem Vector als heightMap hab ich jetzt verstanden (und umgesetzt).

    Ich hab mich jetzt auf das Erstellen von WorldProvider mit new bezogen. Das ist ja nur ein einzelnes Element, wieso bräuchte ich dann da einen vector?

    world = new WorldProvider(winRect.right - winRect.left,    winRect.bottom - winRect.top);
    

    Warum ich eine initialisierungs-methode verwende? Weil die GameTicker Klasse schon ganz am Anfang erstellt wird, da wird allerdings erst noch der Splash-Screen angezeigt. Erst wenn man Enter drückt, wird das Game gestartet und der WorldProvider soll erstellt werden (initGame).


  • Mod

    Ach so. Bei Objekten mit ungewöhnlicher Lebensdauer ist tatsächlich mal einer der Fälle, wo man mal new braucht (aber dann nimm doch einen passenden Smartpointer). Es macht mir aber sehr den Eindruck, dass du hier versuchst, Java-Design auf C++ zu übertragen. Normalerweise erstellt man keine Objekte im Voraus. Warum gibt es also World schon, wenn es erst später initialisiert werden kann?



  • Die Funktion, die die WM_PAINT-message verarbeitet hat eine Instanz von GameTicker zur Verfügung.

    In GameTicker ist gespeichert, welche Phase aktuell gerendert werden soll (PHASE_SPLASH für den Splash-Screen oder PHASE_GAME für die eigentliche Spieloberfläche).

    Wenn gerade das Spiel läuft, wird über WorldProvider der Bildschirm gerendert.
    game.getWorld()->getPipeBounds(i, pipeRect);

    "getWorld" muss ja irgendwo seine WorldProvider-Instanz herbekommen. Und die ist an das GameTicker Objekt gebunden - also ein Member davon.

    Allerdings darf die ja erst da sein, wenn das Game gestartet wird.

    Wie soll ich "world" NICHT im Vorraus "erstellen", wenn es eine Member-Variable ist?


Anmelden zum Antworten