C++: Objekte als Rückgabewert --> löschen?



  • Hallo cooky,

    der Pointer sollte einfach auf das neue Objekt zeigen, und dann den Destruktor aufrufen.

    kompilierbares Minibeispiel (ich musste wg. MS VS Klasse C mit D umbenennen):

    // A.h
    #pragma once
    #include "B.h"
    
    class A
    {
    public:
    	B createBobj()
    	{
    		B bObj(123);
    		return bObj;
    	}
    	A(void);
    	~A(void);
    };
    
    // B.h
    #pragma once
    #include <iostream>
    
    class B
    {
    public:
    	int iZahl;
    	B(int i) {iZahl = i;}
    	~B(void)
    	{
    		std::cout << "Destruktor von B aufgerufen" << std::endl;
    	}
    };
    
    // C.h
    #pragma once
    #include "A.h"
    #include <iostream>
    
    class D
    {
    	A a;
    public:
    	void useB_Obj()
    	{
    		for(;;)
    			std::cout << a.createBobj().iZahl << std::endl;
    
    	}
    	D(void);
    	~D(void);
    };
    
    // main.cpp
    #include "D.h"
    #include <iostream>
    
    int main()
    {
    
    	D d;
    	d.useB_Obj();
    	system ("PAUSE");
    	return 0;
    }
    

    Ausgabe:

    123
    Destruktor von B aufgerufen
    Destruktor von B aufgerufen
    123
    Destruktor von B aufgerufen
    Destruktor von B aufgerufen
    ...
    


  • moon12 schrieb:

    Meinem Verständnis nach werden die Objekte von B auf dem Stack angelegt und zerstört, wenn ihr Scope verlassen wird

    richtig

    - das wäre bei mir der Fall, wenn useBobj() verlassen wird, richtig?

    Falsch
    Nach jdem Schleifendurchlauf wird der Scope verlassen und das Objekt zerstört. Ausserdem kann je nach Optimierung noch beim verlassen der Funktion createBObject ein B zerstört werden.



  • Das heißt ich kann meinen Code so benutzen?
    Ich wusste nicht, dass ein Schleifendurchlauf auch als Scope zählt, dann wäre es ja genau das, was ich brauche:
    neues Objekt zurückliefern, damit arbeiten, objekt zerstören bei neuem Schleifendurchlauf

    edit: selber schuld, eine for-schleife hat ja {} Klammern -.-
    also spricht nichts gegen meinen Code...wenn der 1. Destruktoraufruf durch die Optimierung keinen Einfluss hat?
    und wenn ich das Objekt noch einem Pointer übergebe, macht es denk ich mal sinn, vor Schleifenende ein

    pointerAufObj = NULL;
    

    hinzuzufügen, damit der dann nicht auf das zerstörte Objekt zeigt beim nächsten Schleifenstart ?!



  • moon12 schrieb:

    hinzuzufügen, damit der dann nicht auf das zerstörte Objekt zeigt beim nächsten Schleifenstart ?!

    Ich sehe gar keinen Pointer in deinem Beispiel. 😕



  • da ist ja auch keiner 🙂
    ich meinte, wenn ich später noch einen dazupacke, etwa so:

    // C.h
    #pragma once
    #include "A.h"
    #include <iostream>
    
    class D
    {
        A a;
        B *Pointer;
    public:
        void useB_Obj()
        {
            for(;;)
              {
    [b]
                Pointer = &a.createBobj();
                std::cout << Pointer->iZahl << std::endl;
                Pointer = NULL;
    [/b]
              }  
    
        }
        D(void);
        ~D(void);
    };
    

    Is doch ok oder?



  • 1. Warum solltest du das machen? Wozu?
    2. Nein, das meinte ich mit du weist nicht was du tust. Du hast doch selbst erklärt, dass Objekte nur in ihrem Scope leben. In Zeile 17 zeigt der Pointer auf irgendetwas, aber nicht auf ein gültiges Objekt. Das kann funktionieren, muss aber nicht.
    3. Das NULL setzen am Ende ist völlig sinnlos.



  • ok, vergessen wir das mit dem Pointer auf null setzen.
    Aber dann hab ich Dich wohl an anderer Stelle missverstanden. Ich dachte das Objekt wird NACH jedem Schleifendurchlauf zerstört, warum zeigt der Pointer in Zeile 17 dann nicht auf ein gültiges Objekt?

    Nach jdem Schleifendurchlauf wird der Scope verlassen und das Objekt zerstört.



  • Eben sah dein Code ja auch völlig anders aus. Wenn du eine Funktion aufrufst, gibt die dir ein Objekt zurück. Das lebt aber erst mal nur in dem Ausdruck als rvalue. Wenn du es bis dahin nicht "gerettet" hast, ist es verloren. 😉

    {
      int i = foo();
      cout << i; // ok, Wert kopiert
    }
    {
      int* i = &foo();
      cout << *i; // Nicht ok, das worauf i zeigt lebt gar nicht mehr
    }
    

    Aber du hast die eigentlich interessanteste Frage gar nicht beantwortet: Was sollte das bringen?



  • moon12 schrieb:

    Ich dachte das Objekt wird NACH jedem Schleifendurchlauf zerstört, warum zeigt der Pointer in Zeile 17 dann nicht auf ein gültiges Objekt?

    Du weißt deinem Pointer die Adresse eines temporären Objekts zu. Um dir das zu verdeutlichen.

    Zeile 17:

    a.createBobj() liefert ein temporäres B
    Aufruf des Adressoperators auf das temporäre B
    Zuweisung der Adresse auf den Pointer
    Zerstörung des temporären B

    Zeile 18:
    Zugriff über den Pointer ins zerstörte B -> undefiniertes Verhalten



  • Dem entnehme ich, dass das Objekt nicht nach jedem Schleifendurchlauf, sondern direkt nach Rückgabe zerstört wird.
    D.h. ich darf kein Objekt in einer Funktion erzeugen, sondern muss es z.B. als Member einer Klasse eintragen und dann eine Referenz zurückgeben oder wie kann ich dass dann überhaupt realisieren?



  • moon12 schrieb:

    Dem entnehme ich, dass das Objekt nicht nach jedem Schleifendurchlauf, sondern direkt nach Rückgabe zerstört wird.
    D.h. ich darf kein Objekt in einer Funktion erzeugen, sondern muss es z.B. als Member einer Klasse eintragen und dann eine Referenz zurückgeben oder wie kann ich dass dann überhaupt realisieren?

    Die Annahme ist falsch. Du solltest dich definitiv mal mit den verschiedenen Rückgabetypen befassen und welche stärken und Schwächen sie haben. Du kannst durchaus Temporäre Objekte verwenden, dann musst du sie aber entweder direkt verwenden oder einer lokalen Variable zuweisen, Move Operatoren und Move Konstruktoren sind da sehr hilfreich. Desweiteren könntest du das Objekt auch mit new erzeugen und einen Zeiger zurückliefern. Mit new erzeugte Objekte landen nicht auf dem Stack, allerdings würde sich dort auch ein SmartPointer anbieten, wo wir wieder beim Move wären.


Anmelden zum Antworten