Destruktor wird zweimal aufgerufen



  • Hallo,

    ich habe folgendes Problem: der Destruktor der Klasse "TextBox" (siehe unten) wird mehrmals (2x) aufgerufen. Das sorgt dafür, dass, wenn ich in der Funktion "handle" eine Variable nutze (egal ob Konsolenausgabe oder Änderung) das ganze Spiel beim Aufruf des Destructors einfach abstürzt.
    Meldung: "CrownCatcher2.exe funktioniert nicht mehr". Ich habe das schon durch den Debugger laufen lassen, nur dass meine manuelle Suche da ein genaueres Ergebnis geliefert hat 😉
    Zu Demonstrationszwecken habe ich die unwichtigen Funktionen gelöscht, weil die nichts mit dem Fehler zu tun zu haben scheinen. Nur in der "handle" habe ich mal eine Variable in der Konsole ausgegben, was den Absturz dann verursacht. Auch im Destruktor habe ich einen Text ausgegeben, wodurch deutlich wird, dass er 2x aufgerufen wird. Meine Vermutung:
    Beim 2. Mal wird versucht, Variablen zu löschen, die nicht mehr existieren.

    Hier mal die Klasse, mit der ich das Problem habe:

    Textbox.h (stark gekürzt):

    #ifndef TEXTBOX_H
    #define TEXTBOX_H
    
    #include "GUI.h"
    
    namespace SGui
    {
    	/**
    	*	\brief Textbox class.
    	*
    	*	\class Textbox.
    	*	This class inherits of Widget class.
    	*   
    	*/
    	class Textbox : public HePr::GUI, public sf::RectangleShape
    	{
    	public:
    		Textbox(float x, float y);
    		~Textbox();
    
    		void Handle(const sf::Event& event);
    
    	private:
    		bool m_clicked;
    	};
    }
    #endif // !TEXTBOX_H
    

    Textbox.cpp (stark gekürzt):

    #include "include\Textbox.h"
    
    namespace SGui
    {
    	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    	Textbox::Textbox(float x, float y)
    	{
    		m_clicked = false;
    
    	}
    
    	void Textbox::Handle(const sf::Event& event)
    	{
    		std::cout << m_clicked << std::endl;
    	}
    
    	Textbox::~Textbox()
    	{
    		std::cout << "Destructor" << std::endl;
    	}
    }
    

    Der Code selber stammt NICHT von mir, ich habe ihn nur auf mein Projekt angepasst. Programmiert mit C++ und der SFML, was die Klassen des "sf" Namespaces erklärt. Das hat allerdings nichts mit dem Problem zu tun, wollte ich nur noch mal geschrieben haben, um mögliche Fragen vorzubeugen.

    Ich hoffe, mir kann da jemand einen Tipp geben, was da falsch sein könnte.

    Danke,
    Hegad



  • Best practice ist, ein

    bool m_deleted;
    

    hinzuzufügen und so ein double-delete abzufangen.



  • pp/s schrieb:

    Best practice ist, ein

    bool m_deleted;
    

    hinzuzufügen und so ein double-delete abzufangen.

    Ich habe noch nie mit Exeptions gearbeitet, wie genau geht das mit dem double-delete?



  • Wie du schon selbst sagst, liegt das Problem nicht im gezeigten Code. Setze dich mit dem Debugger in den Destruktor und finde heraus, wo er Aufgerufen wird.

    Allgemein: benutze Smartpointer, wenn du Objekte auf dem Heap anlegen musst.



  • pp/s schrieb:

    Best practice ist, ein

    bool m_deleted;
    

    hinzuzufügen und so ein double-delete abzufangen.

    Schwachsinn!


  • Mod

    Klingt für mich nach dem ersten Lesen nach einer Kombination aus unbeabsichtigten Kopien und Verletzung der Regel der großen Drei.



  • SeppJ schrieb:

    Klingt für mich nach dem ersten Lesen nach einer Kombination aus unbeabsichtigten Kopien und Verletzung der Regel der großen Drei.

    Welche Regel meinst du?



  • Die Regel der großen Drei.

    Wenn du das googelst, wirst du etwas finden.



  • Hegad schrieb:

    SeppJ schrieb:

    Klingt für mich nach dem ersten Lesen nach einer Kombination aus unbeabsichtigten Kopien und Verletzung der Regel der großen Drei.

    Welche Regel meinst du?

    Die Regel. Die Regel der großen Drei/Fünf/Null. Die Regel die man beachten muss, wenn man Klassen schreibt, die Resourcen verwalten.
    Sie lautet:
    Schreibt man einen aus Destruktor, CopyCtor, CopyAssignment, [MoveCtor, MoveAssignment - seit C++11], müssen alle anderen auch geschrieben oder verboten werden.
    Missbeachtung führt eben zu solchen Fehlern, nämlich dass nach einer Kopie zweimal der Destruktor auf der selben Resource aufgerufen wurde, da der default-CopyCtor nur eine shallow copy macht, du aber deep copien musst.
    Da das allerdings aufwändig ist, gibt es für Anfänger die Regel der großen 0:
    Schreibt man einen eigenen Destruktor, CopyCtor, CopyAssignment, [MoveCtor, MoveAssignment - seit C++11], macht man etwas falsch. Man sollte einen RAII Wrapper der Standardbibliothek verwenden. Bspw. einen unique_ptr für Resourcen, die man nicht kopieren kann, kombiniert mit custom deleter, der die Zerstörung übernimmt, oder shared_ptr, wenn man Kopien braucht, der implementiert Reference Counting, oder einen Container anstatt einem dynamischen new[] Array.



  • Quelle für Nathans Kommentar: z.B. hier



  • Nathan schrieb:

    Schreibt man einen eigenen Destruktor...macht man etwas falsch.

    Für virtuelle polymorphe Basisklassen ist ein virtual dTor aber ganz hilfreich.


  • Mod

    Jockelx schrieb:

    Nathan schrieb:

    Schreibt man einen eigenen Destruktor...macht man etwas falsch.

    Für virtuelle Basisklassen ist ein virtual dTor aber ganz hilfreich.

    Dann sagen wir es in diesem Fall lieber "einen eigenen, nicht-virtuellen, nicht-leeren Destruktor".



  • class VirtualFoo
    {
    public:
        virtual ~VirtualFoo() = default;
    };
    

    edit: Schlüsselwort virtual vergessen -.-'



  • Jockelx schrieb:

    Nathan schrieb:

    Schreibt man einen eigenen Destruktor...macht man etwas falsch.

    Für virtuelle Basisklassen ist ein virtual dTor aber ganz hilfreich.

    Und copy/mov bekannterweise verboten -> keine Ausnahme.



  • @Jockelx
    Meinst du wirklich virtuelle Basisklassen (virtuelle Vererbung) oder einfach polymorphe Basisklassen?



  • Ja, danke, meinte polymorph.

    Skym0sh0 schrieb:

    class VirtualFoo
    {
    public:
        ~VirtualFoo() = default;
    };
    

    Ist das so!? Weiss ich jetzt nicht genau, aber nach meinem Wissen erzeugt er den, den er erzeugen würde, wenn man nichts angibt (also nicht virtual).



  • Jockelx schrieb:

    Skym0sh0 schrieb:

    class VirtualFoo
    {
    public:
        ~VirtualFoo() = default;
    };
    

    Ist das so!? Weiss ich jetzt nicht genau, aber nach meinem Wissen erzeugt er den, den er erzeugen würde, wenn man nichts angibt (also nicht virtual).

    Das ist wohl ziemlich sicher so.
    Frage: Ist

    class VirtualFoo
    {
    public:
        virtual ~VirtualFoo() = default;
    };
    

    erlaubt?
    (GCC frisst es zumindest)



  • Jockelx schrieb:

    Ja, danke, meinte polymorph.

    Skym0sh0 schrieb:

    class VirtualFoo
    {
    public:
        ~VirtualFoo() = default;
    };
    

    Ist das so!? Weiss ich jetzt nicht genau, aber nach meinem Wissen erzeugt er den, den er erzeugen würde, wenn man nichts angibt (also nicht virtual).

    Well, fuck 😃

    Ich hab doch echt das virtual vergessen 😃

    hustbaer schrieb:

    (GCC frisst es zumindest)

    MSVC auch


  • Mod

    hustbaer schrieb:

    Frage: Ist [..] erlaubt?
    (GCC frisst es zumindest)

    Ja.


Log in to reply