C++ Objekt wird zerstört, doch warum?

maxmoon

Well-known member
ID: 49665
L
28 August 2006
526
34
Hallo Leute,

habe ein kleines Problem bei der Überladung vom Zuweisungsoperator:


Code:
//Warum kickt er einCFeld weg, wenn man ohne & (Referenz) arbeitet?
const CFeld & CFeld::operator=(const CFeld &cf) {
    int i;

    dim = cf.dim;
    p = new char[cf.dim];
    for (i = 0; i < cf.dim; i++){
        p[i] = cf.p[i];
    }
    cout << "Aufruf von operator=\n";
    return cf;
}

Der Destruktor sieht so aus:
Code:
~CFeld() {
        delete[] p;
    }

Wenn ich der überladenen Methode keine Referenz (&) übergebe, dann wird der Destruktor nach dieser Methode aufgerufen. Arbeite ich hingegen mit der Referenz (&), dann wird kein Destruktor aufgerufen.

Meine Frage ist nun, warum der Destruktor aufgerufen wird, wenn ich ohne Referenz (also mit einer Kopie) arbeite?

Würde mich freuen, wenn mir jemand das Brett vorm Kopf nehmen könnte :biggrin:
 
Meine Frage ist nun, warum der Destruktor aufgerufen wird, wenn ich ohne Referenz (also mit einer Kopie) arbeite?
Weil die Funktion an der Stelle wo du sie verlässt nun mal zu Ende ist, dass heißt alle Variablen der Funktion werden vernichtet.

Die wievielte eigene String Klasse ist das eigentlich...

Prüf doch mal ob deine deine Referenz nicht etwa mit this identisch ist.
 
Weil die Funktion an der Stelle wo du sie verlässt nun mal zu Ende ist, dass heißt alle Variablen der Funktion werden vernichtet.

Die wievielte eigene String Klasse ist das eigentlich...

Prüf doch mal ob deine deine Referenz nicht etwa mit this identisch ist.

Danke für eure Antworten.

Aber selbst wenn ich nicht mit der Referenz arbeite (also mit einer Kopie) und diese Kopie in ein vorhandenes Array reinkopiere, dann müssten die Werte doch in diesem Array gespeichert sein. Es ist doch egal ob die übergebene Kopie nach der Funktion vorhanden ist oder nicht, ich habe diese doch gesichert oder habe ich jetzt einen totalen Denkfehler?
 
Wie erstellst du denn die Kopie? Dafür bräuchtest einen "Kopierkonstruktor" für die Klasse, ansonsten verwenden beide Objekte intern weiterhin die selben Speicherbereiche. Stirbt das eine, ist die "Kopie" auch unbrauchbar.
 
2 cents

Um die Antworten von burnred (für Anfänger) nochmal etwas verständlicher auszuformulieren. Es gibt zwei Arten der Parameterübergabe, call-by-value und call-by-reference. (Die Zweite ist die mit dem &.) ...

call-by-value
Bei call-by-value wird zu Beginn der Funktion ein neues temporäres Objekt cf vom Typ CFeld erstellt, welches beim Verlassen der Funktion auch wieder zerstört wird. Die Funktion hat also keinen direkten Zugriff auf das an sie übergebene Objekt sondern sieht lediglich eine Kopie von ihm.

PHP:
CFeld &CFeld::operator=(CFeld cf) { ... }

int main(void)
{
    CFeld a,b;
    // a = b; bedeutet hier soviel wie
    {
        CFeld cf(b); // Copy-Konstruktor!
    ...
        for(i=0; i<cf.dim; ++i) { a.p[i] = cf.p[i]; }
    ...
    }
    return 0;
}

Vorteil: Das kopierte Objekt kann innerhalb der Funktion beliebig bearbeitet werden, ohne das ursprünglich an die Funktion übergebene Objekt zu beeinflussen.
Achtung: Performance-Verlust durch Erstellung der Kopie.
Wichtig: Um die Kopie von CFeld erstellen zu können muss für CFeld ein Copy-Konstruktor CFeld::CFeld(const CFeld &other) definiert sein! Stichworte sind hier flache Kopie und tiefe Kopie. Funktioniert der Copy-Konstruktor nicht einwandfrei (oder fehlt), kommt es zu den beschriebenen Effekten. Durch Erstellung einer fehlerhaften Kopie gibt es plötzlich zwei Objekte, welche den gleichen Speicher benutzen. Soll soviel bedeuten, wie beide Objekte haben zwar einen eigenen Pointer, dieser zeigt jedoch auf den gleichen Speicherbereich. Wird nun eines dieser Objekte beim verlassen der Funktion zerstört, gibt es "seinen" Speicher frei und verstümmelt dabei das andere Objekt.


call-by-reference
Mit call-by-reference hingegen bekommt die Funktion direkten Zugriff auf das übergebene Objekt. Es werden keine Kopien angefertigt! Immer wenn innerhalb der Funktion die Referenz cf verwendet wird, wird in Wirklichkeit dirket das an die Funktion übergebene Objekt verwendet.

PHP:
CFeld &CFeld::operator=(const CFeld &cf) { ... }

int main(void)
{
    CFeld a,b;
    // a = b; bedeutet hier soviel wie
    {
    ...
        for(i=0; i<b.dim; ++i) { a.p[i] = b.p[i]; }
    ...
    }
    return 0;
}

Vorteil: Bessere Performance, da das übergebene Objekt nicht kopiert werden muss. Kann auch als für zusätzliche „return“ Werte genutzt werden.
Achtung: Objekte, die als Referenz übergeben werden können durch die Funktion verändert werden!


Gängige Implementierung
Bei einer Zuweisung halte ich es nicht für sinnvoll, eine konstante Referenz als Rückgabewert zu verwenden. Und, wie bereits erwähnt, ist es beim call-by-reference-Zuweisungsoperator es meist sinnvoll, mögliche Selbstzuweisungen zu vermeiden (oder zumindest zu bedenken.)
PHP:
CFeld &CFeld::operator=(const CFeld &cf)
{
    // Nur weiter, wenn es keine Selbstzuweisung ist.
    if( &cf != this ) {
        // Hier erfolgt die Zuweisung.
    }
    return (*this); // Eine Referenz auf sich selbst ...
}


Anmerkungen
zum Code aus dem ersten Posting. Bei einer call-by-value-Implementierung sollte man keine Referenz auf ein Funktionsargument zurückgeben, da dieses beim Verlassen der Funktion - also noch bevor es überhaupt verwendet werden kann - schon wieder zerstört wird.

Und was passiert bei der Zuweisung mit dem alten Speicher? Sollte das "alte" p nicht zuerst gelöscht werden? Und was würde hier bei einer Selbstzuweisung, also p == cf.p, passieren?