destruktive Destruktoren



  • Hallo!
    Auch nach jahrelanger dilletantischer Gelegenheits-C++-Programmiererei begreife ich wohl immer noch nicht, wie wann warum Destruktoren aufgerufen werden. Jedenfalls machen die mir immer ganz unerwartet alles kaputt.

    Die Klasse Neuron sieht in Auszügen so aus:

    class Neuron
    {
    public:
    	double *weight;
    	bool initialized;
    	bool stored;
    	Link **input;
    	int nInput;
    
    	Neuron()
    	{
    		initialized=false;
    		stored=false;
    		input=NULL;
    		weight=NULL;
    		nInput=0;
    	}
    	Neuron(int size, Link **links, bool store)
    	{
    		nInput=size;
    		if (store)
    		{
    			stored=true;
    			input=new Link*[SIZE];
    			memcpy(input, links, sizeof(Link *)*SIZE);
    		}
    		else
    		{
    			stored=false;
    			input=links;
    		}		
    		weight=new double[nInput];
    		printf("    Neuron(parameters)\n        weight address %d allocated\n", weight);
    		initialized=true;
    	}
    	Neuron operator=(const Neuron &other)
    	{
    		destroy();
    		if (other.initialized)
    		{
    			nInput=other.nInput;
    			if (other.stored)
    			{
    				stored=true;
    				input=new Link*[SIZE];
    				memcpy(input, other.input, sizeof(Link *)*SIZE);
    			}
    			else
    			{
    				stored=false;
    				input=other.input;
    			}
    			weight=new double[nInput];
    			printf("    operator=\n        weight address %d allocated\n", weight);
    			memcpy(weight, other.weight, sizeof(double)*nInput);
    
    			initialized=true;
    		}
    		return *this;
    	}
    	void destroy()
    	{
    		if (initialized)
    		{
    			if (weight!=NULL)
    				delete[] weight;
    			if (stored)
    				delete[] input;
    		}
    		initialized=false;
    		weight=NULL;
    		stored=false;
    		input=NULL;
    		nInput=0;
    	}
    	~Neuron()
    	{
    		printf("    destructor\n        weight adress: %d\n", weight);
    		destroy();
    	}
    
    };
    

    Und hier wird sie benutzt:

    // global definiert:
    Neuron neurons[SIZE][SIZE];
    
    // ... blah blah ...
    
    // später dann irgendwo im Programm:
    	for (int i=0; i<SIZE; i++)
    		for (int j=0; j<SIZE; j++)
    		{
    		// ... blah ...
    			printf("blah\n");
    			neurons[i][j]=Neuron(SIZE*SIZE, links, false);
    			printf("blub\n");
    		// ... blah ...
    		}
    

    Das Problem ist jetzt, dass direkt nach "neurons[i][j]=..." offenbar der Destruktor für neurons[i][j] aufgerufen wird. Also nicht nur für den rechten Operanden, bei dem das ja klar ist.

    Die Ausgabe an der Stelle ist:

    blah
        Neuron(parameters)
            weight address -2063630320 allocated
        operator=
             weight address -2063495152 allocated
        destructor
            weight adress: -2063495152
        destructor
            weight adress: -2063630320
        blub
    

    Ich hab echt keine Ahnung, warum der Destruktor da gleich aufgerufen wird. Das macht doch keinen Sinn??
    Sorry für den langen Code-Abschnitt mit der Neuron-Klasse. Aber da muss ja wohl irgendwo der Fehler drin stecken und ich hab keine Ahnung, wo.

    Grüße
    Krecik


  • Mod

    Dein Zuweisungsoperator gibt ein neues Objekt zurück, dazu wird der Copy-Ctor - den du nicht deklariert hast - aufgerufen. Dieser vom Compiler implizit generierte Ctor kopiert einfach jeden Member, das resultierende Objekt wird nach der Rückkehr aus dem Zuweisungsoperator nat. gleich wieder zerstört.
    Hier läuft ohnehin einiges falsch - wie copy-ctor und Zueweisungsoperator zu schreiben sind, ist regelmäßig Thema in diesem Forum, deshalb gehe hier nicht darauf ein.



  • Krecik schrieb:

    ...nach jahrelanger dilletantischer Gelegenheits-C++-Programmiererei...

    Ohne böse sein zu wollen, genau so sieht dein Code auch aus. Was willst du mit dieser Klasse eigenlich erreichen? Zudem scheinst du eine absolute Vorliebe für Zeigerarithmetik zu haben (womit du dir das Leben nur unnötig schwer machst).

    Vielleicht kann man dir helfen in dem du mal beschreibst was du mit dieser Klasse erreichen willst (und Bitte: printf, public Membervariablen, memcpy [mit Ausnahme von Absoluten Sonderfällen] gehören nicht in sauberes C++). Zudem sollte ein Konstruktor immer einen sauberen Initialzustand haben (und warum verwendest du eine Methode destroy obwohl es in den Destruktor gehört?).

    Ganz davon abgesehen: Der Destruktor wird am Ende der Lebenszeit aufgerufen (Sprich: falls du das Element lokal angelegt hast am Ende des Scopes und bei dynamischen beim delete). Die Membervariablen (sofern sie nicht statisch sind) werden dadurch ungültig, so das du diese auch nicht auf Initialwerte setzen musst. Zudem hat delete eine Automatische Prüfung gegen NULL, sofern du nicht anschließend mit dem Zeiger noch was anstellst kannst du dir somit sowohl das if, als auch das = NULL sparen.

    cu André


  • Mod

    Wenn ich das ein bisschen zusammenfasse, komme ich ungefähr auf

    class Neuron
    {
    public:
        typedef std::pair< double, Link* > link_type;
        typedef std::vector< link_type > links_type;
        links_type links;
        Neuron() : links() {}
        explicit Neuron(const links_type& links)
            : links( links )
        {}
    };
    

    Hier passiert also nicht viel - kein Grund dafür 100 Zeilen fehlerhaften Code zu verschwenden.
    initialized und stored sind sowieso überflüssig, nInput korrespondier mit links.size()
    ich bin einfach mal davon ausgegangen, dass korrespondierende weight und Links-Elemente zusammengehören.



  • achja und nicht vergessen wenn stored == true musst du auch deine input dinger da freigeben ... std::memcpy bei Klassen nicht nutzen! Usw. ...



  • Krecik schrieb:

    bool initialized;

    Nicht destruktoren sind dein Problem, sondern generell Initialisierungen.
    Der Konstruktor sollte initialisieren, naemlich das was dein operator= und dein nicht Default-Konstruktor macht.

    Indem du diese initialized-Variable wegwirfst, reduzierst du die Komplexitaet enorm.

    Im Prinzip ist dein destroy() genau das was ein Destruktor macht - nur dass du eben mit dem initialized komplett gegen diese Mechanik arbeitest. Der Fehler in deinem Code liegt aber in der Verletzung der Regel der grossen 3.

    Kleiner Tip:
    achte mal auf den Rueckgabetyp vom operator== 😉



  • Shade Of Mine schrieb:

    Im Prinzip ist dein destroy() genau das was ein Destruktor macht - nur dass du eben mit dem initialized komplett gegen diese Mechanik arbeitest.

    Deswegen wohl auch destruktive Destruktoren im Topic 🤡 🤡



  • sieht nach einem neuron in einem neuronalen netz aus, kleiner tipp
    wenn ein nulti-layer-perceptron werden soll, dann nimm als kleinste
    einheit eine ganze schicht, und nicht ein einzelnens neuron

    hab auch mal versucht die struktur bis auf die einzelnen neuronen runterzubrechen, und glaub mir, das macht nur ärger ...



  • Hmm, wo ist denn mein Beitrag von gestern geblieben? Hab ich den wohl gar nicht abgeschickt. Ärgerlich.
    Deshalb jetzt nur noch mal ganz kurz. Also, ich wollte eigentlich nur sagen: Vielen Dank für die Hilfe von allen. Hab einfach aus dem "Neuron operator=" ein "Neuron &operator=" gemacht. Das hilft erst mal. Obwohl void natürlich auch geholfen hätte, zumal ich auch gar keinen Rückgabewert brauche. Ich hoffe, ich werd das mal im Kopf behalten, wie das funktioniert.
    Mein Programm werde ich wohl in die Tonne kloppen, weil ich eh kein Konzept hatte und von vornherein gar nicht wusste, was ich eigentlich vorhatte.
    Zu den Programmierstil-Belehrungen: Ich weiß, dass ich furchtbar schlechten Code schreibe. Aber da bin ich einfach ein hoffnungsloser Chaot. Trotzdem vielen Dank für die Ratschläge, von denen hoffentlich wenigstens ein kleiner Bruchteil nicht umsonst war und in meinem porösen Hirn einen Platz findet, den ich beizeiten wiederfinde. 🙂

    Viele Grüße
    Unkraut


Anmelden zum Antworten