Frage zu new im Zusammenhang mit Konstruktor- bzw. Destruktoraufruf[gelöst]



  • Hallo,

    ich habe folgendes Quelltextbeispiel:

    #include <iostream>
    using namespace std;
    
    class X
    {
    public:
    	X()
    	{
    		cout << "Konstuktor von X wird aufgerufen.\n";
    	}
    
    	~X()
    	{
    		cout << "Destruktor von X wird aufgerufen.\n";
    	}
    };
    
    void f1(X x)
    {
    	X* XPtr = new X;
    }
    
    int main()
    {
    	X XObjekt;
    
    	X* XPtr = new X;
    
    	f1(XObjekt);
    
    	return 0;
    }
    

    Dieses führt zu folgender Ausgabe:

    Konstuktor von X wird aufgerufen.
    Konstuktor von X wird aufgerufen.
    Konstuktor von X wird aufgerufen.
    Destruktor von X wird aufgerufen.
    Destruktor von X wird aufgerufen.
    

    Meine Frage ist nun, warum führt die Anweisung X XPtr = new X;* dazu, dass der Konstruktor von X aufgerufen wird aber nicht der Destruktor? Denn wenn man sich die Anweisung f1(XObjekt); betrachtet und was die Funktion macht, dann wundert es mich, dass dort der Destruktor aufgerufen wird aber in der vorherigen Anweisung nicht. Für Erläuterungen, Denkanstöße oder Hinweise bin ich sehr dankbar 🙂



  • An welcher Stelle sollte denn deiner Meinung nach genau der Destruktor aufgerufen werden und wieso? Wieso genau wundert es dich, dass er beim Aufruf der Funktion aufgerufen wird? 😉



  • Ich hätte erwartet, dass auch die Anweisung X* XPtr = new X; dazu führt, dass der Destruktor für X aufgerufen wird, dies passiert jedoch nicht. Jedoch passiert das ganze wenn ich das Objekt als Parameter an f1 übergebe und es dort dynamisch erstelle und dort liegt mein Verständnisproblem, wieso wird in diesem Fall dann der Destruktor aufgerufen und für die andere Anweisung nicht, das bereitet mir etwas Kopfzerbrechen 😉

    EDIT: Ist die Funktionsweise deshalb so, dass der Destruktor im Falle von f1 aufgerufen wird, da die Funktion verlassen wird, das Objekt nicht static ist und somit "zerstört" wird? Das wäre mein Ansatz für diese Verhaltensweise.



  • Der Sinn von new ist ja, dass DU den Destruktur (über delete) selbst aufrufen musst. Es heißt ja nicht zum Spaß manuelle Speicherverwaltung. Wenn der Destruktor von sich aus aufgerufen werden würde, wäre es ja nicht mehr manuell.



  • Das ist mir bewusst und erklärt logisch, warum die zweite Anweisung der main-Funktion keinen Destruktoraufruf verursacht. Aber warum macht dies die dritte Anweisung? Hier wird ebenfalls kein delete verwendet aber trotzdem ein Destruktor aufgerufen.



  • Raw schrieb:

    Das ist mir bewusst und erklärt logisch, warum die zweite Anweisung der main-Funktion keinen Destruktoraufruf verursacht. Aber warum macht dies die dritte Anweisung? Hier wird ebenfalls kein delete verwendet aber trotzdem ein Destruktor aufgerufen.

    Der zweite Destruktoraufruf kommt nicht von dem in f1() per new erzeugten Objekt. f1() bekommt als Parameter ein X übergeben. Daher wird eben beim Aufruf der Funktion ein X, nämlich der Funktionsparameter x konstruiert (durch kopieren von XObject), welches nach Rückkehr der Funktion wieder zerstört wird...



  • Vielen Dank für die Erklärung und die wirklich schnellen Antworten! 🙂



  • ^ Was dot schreibt. Ich denke auch das Programm ist irreführend konstruiert, weil man denkt es würden 3 Objekte erstellt. Es werden allerdings vier Objekt erstellt, was man leicht sieht wenn man auch den Kopierkonstruktor selbst schreibt: https://ideone.com/6xnHk2

    #include <iostream> 
    using namespace std;
    
    class X 
    { 
    public: 
        X() 
        { 
            cout << "Konstuktor von X wird aufgerufen.\n"; 
        }
    
        X(const X& other)
        {
        	cout << "Kopierkonstruktor von X wird aufgerufen.\n"; 
        }
    
        ~X() 
        { 
            cout << "Destruktor von X wird aufgerufen.\n"; 
        } 
    }; 
    
    void f1(X x) 
    { 
        X* XPtr = new X; 
    } 
    
    int main() 
    { 
        X XObjekt;
        X* XPtr = new X;
        f1(XObjekt);
    }
    

    Ausgabe:

    Konstuktor von X wird aufgerufen.
    Konstuktor von X wird aufgerufen.
    Kopierkonstruktor von X wird aufgerufen.
    Konstuktor von X wird aufgerufen.
    Destruktor von X wird aufgerufen.
    Destruktor von X wird aufgerufen.


  • Mod

    Noch eine Spur deutlicher:

    #include <iostream>
    using namespace std;
    
    class X
    {
      static int counter;
      int number;
    public:
      X() : number(counter++)
      {
        cout << "Konstuktor von Objekt " << number << " wird aufgerufen.\n";
      }
    
      X(const X& other): number(counter++)
      {
        cout << "Kopierkonstruktor wird aufgerufen. "
          "Kopiertes Objekt: " << other.number
             << " Neues Objekt erhält Bezeichnung " << number << '\n';
      }
    
      ~X()
      {
        cout << "Destruktor von " << number << " wird aufgerufen.\n";
      }
    };
    
    int X::counter = 0;
    
    void f1(X x)
    {
        X* XPtr = new X;
    }
    
    int main()
    {
        X XObjekt;
        X* XPtr = new X;
        f1(XObjekt);
    }
    
    Konstuktor von Objekt 0 wird aufgerufen.
    Konstuktor von Objekt 1 wird aufgerufen.
    Kopierkonstruktor wird aufgerufen. Kopiertes Objekt: 0 Neues Objekt erhält Bezeichnung 2
    Konstuktor von Objekt 3 wird aufgerufen.
    Destruktor von 2 wird aufgerufen.
    Destruktor von 0 wird aufgerufen.
    

Anmelden zum Antworten