Inhaltsverzeichnis[Anzeigen]

 

Speicherverwaltung mit auto_ptr

nach "The C++ Standard Library" von Nicolai M. Josuttis

  • Die C++ standard library enthält "smart pointern" - die auto_ptr -, um die Speicherverwaltung zu vereinfachen.
  • auto_ptr reichen das Interface des Pointers durch, den sie besitzen
  • die auto_ptr folgen dem RAII Idiom "resource acquisition is initialization" , bei dem die Resssourcebeanspruchung und Ressourcefreigabe an lokale Objekte gebunden wird (vgl. dazu Bjarne Stroustrup )

Motivation

void f(){
ClassA* ptr= new ClassA;

// tue etwas mit ptr
delete ptr;
}
  • dieser Code hat zumindestens zwei Probleme:
    • return statement zwischen new und delete
    • eine Ausnahme kann geworfen werden
  • beide Punkte führen zu einem Speicherloch
  • Lösungsversuch
void f(){

ClassA* ptr= new ClassA;
try{
// tue etwas mit ptr

}
catch( ... ){
delete ptr;

throw;
}
delete ptr;
}
  • der Code wird komplizierter und redundant
  • falls ein neues Objekt entsprechend gesichert werden soll, wird' s noch komplizierter
  • das Löschen eines null Pointers hat keine Auswirkung, falls schon das Speicher allozieren schieft geht
  • MOVED TO... auto_ptr als Lösung:
void f(){
std::auto_ptr<classA> ptr(new ClassA);

....
};
  • auto_ptr fungiert hier als lokales Objekt
  • sobald ptr zerstört wird, zerstört auto_ptr sein Objekt new ClassA
#include <memory>
#include <string>

void bad(){
std::string* ptr= new std::string;
}

void good(){
std::auto_ptr< std::string > ptr(new std::string);
}

int main(){
bad();
good();
}
  • ALERT! der auto_ptr besitzt sein Objekt bzw. ein Objekt sollte maximal einen auto_ptr als Besitzer haben ALERT!
  • der auto_ptr will explizit sein Objekt erhalten:
    • std::auto_ptr ptr(new ClassA);   //OK
    • std::auto_ptr ptr= new ClassA;   //Fehler
    • std::auto_ptr ptr1( new ClassA);
    • std::auto_ptr ptr2( new ClassA); ptr1=ptr2 // OK
    • std::auto_ptr ptr; ptr= new ClassA   //Fehler

Transfer of Ownership

  • für auto_ptr gilt das Prizip der "strict ownership":
  • MOVED TO... durch den Kopierkonstruktor und den Zuweiungsoperator wechselt der Besitz

Kopierkonstruktor

std::auto_ptr<ClassA> ptr1(new classA);

std::auto_ptr<classA> ptr2(ptr1);

Zuweisungsoperator

std::auto_ptr<ClassA> ptr1(new classA);
std::auto_ptr<classA> ptr2;

ptr2= ptr1;
  • in beiden Fällen geht der Besitz an ptr2 über und ptr1 ist danach ein Nullzeiger
  • ALERT! bei beiden Operation wird das Ursprungsobjekt modifiziert; d.h. die rechte Seite der Zuweisung wird modfiziert ALERT!
  • MOVED TO... der Zuweisungsoperator und der Kopierkonstruktor erhalten nicht konstante Parameter:
auto_ptr( auto_ptr&) throw();

auto_ptr& operator= (auto_ptr&) throw();
  • durch
std::auto_ptr<ClassA> ptr1(new classA);
std::auto_ptr<classA> ptr2(new classA);

ptr2= ptr1;
  • wird ptr2 urspüngliches Objekt gelöscht

Source and Sink

Funktion als Senke von Daten

void sink( std::auto_ptr<ClassA> data);
  • sink wird zur Senke der Daten
  • sink gehört nun data und beim Beenden von sink wird data inklusive seines Besitzers gelöscht

Funktion als Quelle von Daten

std::auto_ptr<classA> source(){

std::auto_ptr<classA> ptr(new ClassA);
....
return ptr;
}

void g(){
std::auto_ptr<classA> p;

for ( int i=0; i<10; ++i){

p= source();
....
}
}
  • ptr inklusiver seiner Daten werden beim rauskopieren gelöscht
  • erhält p von der Quelle source die Daten
  • durch die neue Zuweisung in der for Schleife wird p's ursprüngliche Objekt gelöscht
  • letztendliche wird auch p und sein Inhalt gelöscht, sobald p out of scope geht

Warnungen

  • folgender Code führt zu einem Laufzeitfehler
template < typename T >

void doSomething( std::auto_ptr<T> p );
....
std::auto_ptr <int> p( new int );

*p= 42;
doSomething( p );
*p= 18;
  • der Speicher von p wird gelöscht, da sein Besitz an doSomething übergeht
template < typename T >

void doSomething( std::auto_ptr<T>& p );
  • die Übergabe durch eine Referenz gilt als schlechtes Design, da die Besitzverhältnisse nicht mehr geklärt sind, den sowohl der aufrufende als auch der aufgerufene scope besitzen nun den auto_ptr
template < typename T >
void doSomething( const std::auto_ptr<T>& p );
  • hingegen wird die Übergabe als eine konstante Referenz explizit durch den Standard unterbunden
  • MOVED TO... da alle Container die Objekte als konstante Referenz annehmen und dann intern erst mal kopieren, können diese keinen auto_ptr als Mitglied halten
  • will man den Besitzübergang des Objekts von auto_ptr unterbinden, kann man diesen als const erklären:
std::auto_ptr<int> f(){
const std::auto_ptr<int> p(new int);

std::auto_ptr<int>q(new int);

*p= 42; // OK

p=q; // Error
q=p; // Error
return p; // Error

}
  • die Zuweisung *p= 42 ist möglich, da hier nur das von p referenzierte Objekt angesprochen wird
  • hingegen setzen p=q; q=p und return p die Besitzübergabe voraus, was auf einem konstanten auto_ptr nicht möglich ist

auto_ptr als Member

class ClassB{
private:
ClassA* ptr1;

ClassA* ptr2;
public:
ClassB( ClassA val1, ClassA val2): ptr1(new ClassA(val1)),ptr2(new ClassA(val2)){}

ClassB (const ClassB& x): ptr1(new ClassA(*x.ptr1)),ptr2(new ClassA(*x.ptr2)){}

const ClassB& operator= (const ClassB& x){

*ptr1= *x.ptr1;
*ptr2= *x.ptr2;

return *this;
}
~ClassB(){ delete ptr1; delete ptr2 }

...
};
  • falls das zweite new in beiden Konstruktoren eine Ausnahme wirft, entsteht eine Speicherloch, da der Destruktor erst dann aufgerufen wird, wenn der Konstruktor fertig durchgeführt wurde
  • hier bieten auto_ptr eine einfache Lösung
class ClassB{
private:
const std::auto_ptr<ClassA> ptr1;
const std::auto_ptr<ClassA> ptr2;

public:
ClassB( ClassA val1, ClassA val2): ptr1(new ClassA(val1)),ptr2(new ClassA(val2)){}

ClassB (const ClassB& x): ptr1(new ClassA(*x.ptr1)),ptr2(new ClassA(*x.ptr2)){}

const ClassB& operator= (const ClassB& x){

*ptr1= *x.ptr1; //
*ptr2= *x.ptr2;

return *this;
}
...
};
  • der Kopierkonstruktor und der Zuweisungsoperator müssen implementiert werden, da die vom Compiler erzeugten die beiden auto_ptr direkt modifizieren würde, was nicht erwünscht und nicht möglich ist:
const ClassB& operator= (const ClassB& x){

ptr1= x.ptr1;
ptr2= x.ptr2;

return *this;
}

Manipulation der Daten

release

T* auto_ptr::release() throw();
  • gibt das Objekt wieder frei
  • als Returnwert erhält man die Adresse des Objekts oder den Nullzeiger

reset

void auto_ptr::reset( T* ptr= 0) throw();
  • der auto_ptr wird mit ptr reinitialisiert
  • das ursprüngliche Objekt wird gelöscht
  • *this ist nun der Besitzer des Objekts

Missbrauch

auto_ptr' s können ihren Besitz nicht teilen:

void tschüs( auto_ptr <int > );

int* i =new int;
std::auto_ptr <int> p1(i);

std::auto_ptr <int> p2(i);
tschüs(p2);
*p1= 30; // undefiniertes Verhalten

auto_ptr' s können keine arrays verwalten, da dieser delete (nicht delete []) aufruft

auto_ptr' s erfüllen nicht die Anforderungen von Containerelementen

Quelle und Ziel einer Zuweisung- oder Kopieroperation ergibt bei auto_ptr verschiedene Objekte MOVED TO... das initiale Kopieren im Container hätte fatale Folgen für die Quelle der Operation

Ergänzungen:

Mentoring

Stay Informed about my Mentoring

 

Rezensionen

Tutorial

Besucher

Heute 902

Gestern 3357

Woche 12465

Monat 39782

Insgesamt 3892496

Aktuell sind 43 Gäste und keine Mitglieder online

Kubik-Rubik Joomla! Extensions

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare