return und new und delete



  • Hallo,
    getData ist zwar richtig, aber kein besonders guter Stil. Du machst den Caller der Funktion für die Bereinigung des Speichers verantwortlich. Das ist nicht nett. Wenn du unbedingt einen Pointer zurückgeben willst, dann würde ich einen auto_ptr nehmen. Dann wird auch garantiert der Speicher freigegeben:

    auto_ptr<myClass> GetData()
    {
        auto_ptr<myClass>pClass(new myClass);
        // mach was
        return pClass;
    }
    


  • @hume
    Das mit den auto_ptr fand ich schon immer etwas komisch. Ich seh nicht ganz den Vorteil gegenüber einer "normalen" lokalen Variable

    Versteh ich das richtig?
    Beim Verlassen der Funktion wird der Destruktor des auto_ptr aufgerufen und der Speicher freigegeben.
    Vorher wird er mittels Kopierkonstruktor kopiert. Dabei muss ja für myClass erneut Speicher reserviert werden auch myClass mittels Kopierkonstruktor kopiert werden.

    Sind das nicht unnötig viele Kopier- und Speicheroperationen? Da kann man ja auch gleich myClass zurückgeben.



  • @kartoffelsack
    Nein, da hast du was falsch verstanden images/smiles/icon_smile.gif

    Beim zurückgeben eines auto_ptr aus einer Funktion (natürlich an einen auto_ptr) wird nur der Besitzer des verwalteten Objekts geändert. Der lokale auto_ptr tut im Destruktor einfach gar nichts. Das Objekt wird dann von dem anderen auto_ptr gelöscht. Somit hast du nicht mehr Konstruktor/Destruktor aufrufe als beim normalen Pointer.

    [ Dieser Beitrag wurde am 07.11.2001 um 14:19 Uhr von HumeSikkins editiert. ]



  • Hatte schon die ganze Zeit gegrübelt, ob ich die auto_ptr-Version angebe, war schließlich nicht die Frage. Wie dem auch sei, ich finde die auto_ptr-Variante auch besser.

    @kartoffelsack
    versuch mal das (Variablen analog dem oberen Beispiel gewählt):
    auto_ptr<int> ptr( new int(4711) );
    cout << ptr.get() << endl; // aktueller Zeiger
    auto_ptr<int> tmp = ptr;
    cout << ptr.get() << endl; // muesste NULL sein
    cout << tmp.get() << endl; // muesste der alte Zeiger sein

    Der Dtor von ptr würde einen NULL-Zeiger löschen und bekanntermaßen passiert nichts.



  • Heißt das, dass ich immer nur eine gültige Kopie das auto_ptr haben kann?

    [ Dieser Beitrag wurde am 07.11.2001 um 14:34 Uhr von kartoffelsack editiert. ]



  • Hallo,
    es darf immer nur ein auto_ptr Besitzer eines Objekts sein. Dies wird durch die Copy-Semantik der auto_ptr-Klasse sichergestellt. Allerdings kannst du natürlich böswillig sowas schreiben:

    A* a = new A;
    auto_ptr<A> Auto1(a);
    auto_ptr<A> Auto2(a);
    

    Beide auto_ptr besitzen nun a. Beide werden a löschen -> KAWUMM images/smiles/icon_smile.gif



  • hallo, kann ich denn mit einem autopointer dann genauso weiterarbeiten, wie ich mit einem normalen pointer arbeiten kann?
    und dann noch vielen dank für den tipp. ich kannte bis dato noch gar keinen autopointer images/smiles/icon_wink.gif



  • Hallo,
    du kannst mt auto_ptr alles machen außer Pointerarithmetik. D.h. ist a ein auto_ptr kannst du nicht sowas wie a++ schreiben. Das macht für auto_ptr aber auch gar keinen Sinn, da sie nicht dazu gedacht sind Zeiger auf Arrays aufzunehmen.



  • Jo, kann man.
    Der auto_ptr befindet sich in <memory>

    #include <memory>
    using namespace std;
    
    // Beim Initialisieren muss der Zeiger in Klammern geschrieben werden, sonst Fehler:
    auto_ptr<int> ptr = new int(5); // Fehler
    auto_ptr<int> ptr( new int(5) ); // OK
    // Mit 'get' bekommt man den Zeiger:
    int* pi = ptr.get();
    // den Wert mit *
    int v = *ptr;
    // Bei Strukturen gibt es außderdem ein ->: auto_ptr<string> ps; ps->length();
    // es gibt noch 2 Funktionen, 
    // reset zum Neusetzen (ähnlich einer Zeigerzuweisung )
    // release (auto_ptr gibt Kontrolle auf, löscht aber nicht, Rückgabe des Zeigers):
    ptr.reset(0); // ruft delete auf
    ptr.release(); // ruft delete NICHT auf, außerdem kontrolliert ptr nicht mehr den Speicher
    int* pi = ptr.release(); // Kontrolle aufgeben, Zeiger merken
    // in den meisten Fällen braucht man diese Funktionen nicht
    

    Wie oben zu sehen: man darf nur einen auto_ptr je Objekt besitzen, sonst !!KRACH!!
    Daraus ergibt sich gleich eine Regel, nur der 'Hauptzeiger' darf ein auto_ptr sein, der Rest sind normale Zeiger. Man kann sich diesen Zeiger als einen Anker vorstellen, wenn der Anker verschwindet, verschwindet auch das zugehörige Objekt. Andererseits, kann man diesen Anker einen anderen in die Hand drücken, wie in deinem Beispiel:
    auto_ptr<myClass> getData() {
    auto_ptr<myClass> ptr( new myclass ); // bis jetzt ist ptr für delete zuständig

    return ptr; // nun wird die Zuständigkeit dem Aufrufer übergeben
    }

    getData(); // da hier keiner den Auto-Zeiger haben will, löscht C++ automatisch die nicht angenomme Rückgabe
    auto_ptr<myClass> tmp = getData(); // nun ist tmp der neue Anker
    // falls eine Funktion einen Zeiger braucht:
    function( tmp.get() ); // Da diese Funktion nicht für die Lebensdauer zuständig ist, bekommt sie einen 'normalen' Zeiger.

    Wenn man diese Technik anwendet, werden die Programme leichter zu schreiben und zu lesen, weniger fehleranfällig und Speicher- und Resourcenlecks treten nicht mehr auf.
    Und später im Zusammenhang mit Exceptions ist diese Technik fast unverzichtbar, aber auch ohne macht sie schon sehr viel Sinn.

    cu

    [ Dieser Beitrag wurde am 07.11.2001 um 18:24 Uhr von Fux editiert. ]



  • @Fux
    kopieren, einfügen, ausdrucken, einheften images/smiles/icon_smile.gif

    [compilerspecific]
    Die nicht standardkonforme auto_ptr-Implementation des VC besitzt keine reset-Methode.
    [/compilerspecific


Anmelden zum Antworten