Angesichts der verschiedenen Änderungen habe ich den Eindruck, dass eine umfassende Zusammenfassung nützlich wäre.
1. Wenn nicht
Es gibt zwei Situationen, in denen Sie keine intelligenten Zeiger verwenden sollten.
Das erste ist genau die gleiche Situation, in der Sie a nicht verwenden sollten C++ tatsächlich Klasse verwenden . IE: DLL-Grenze, wenn Sie dem Client den Quellcode nicht anbieten. Sagen wir anekdotisch.
Das zweite passiert viel häufiger: Smart Manager bedeutet Eigentum . Sie können Zeiger verwenden, um auf vorhandene Ressourcen zu verweisen, ohne deren Lebensdauer zu verwalten. Beispiel:
void notowner(const std::string& name)
{
Class* pointer(0);
if (name == "cat")
pointer = getCat();
else if (name == "dog")
pointer = getDog();
if (pointer) doSomething(*pointer);
}
Dieses Beispiel ist eingeschränkt. Ein Zeiger unterscheidet sich jedoch semantisch von einer Referenz darin, dass er auf eine ungültige Position (den Nullzeiger) verweisen kann. In diesem Fall ist es vollkommen in Ordnung, stattdessen keinen intelligenten Zeiger zu verwenden, da Sie die Lebensdauer des Objekts nicht verwalten möchten.
2. Intelligente Manager
Wenn Sie keine Smart Manager-Klasse schreiben und das Schlüsselwort verwenden, machen delete Sie etwas falsch.
Es ist eine kontroverse Sichtweise, aber nachdem ich so viele Beispiele für fehlerhaften Code überprüft habe, gehe ich kein Risiko mehr ein. Wenn Sie also schreiben new, benötigen Sie einen intelligenten Manager für den neu zugewiesenen Speicher. Und du brauchst es jetzt.
Das bedeutet nicht, dass Sie weniger Programmierer sind! Im Gegenteil, die Wiederverwendung von Code, der nachweislich funktioniert, anstatt das Rad immer wieder neu zu erfinden, ist eine Schlüsselkompetenz.
Nun beginnt die eigentliche Schwierigkeit: Welcher Smart Manager?
3. Intelligente Zeiger
Es gibt verschiedene intelligente Zeiger mit verschiedenen Eigenschaften.
Überspringen, std::auto_ptrdas Sie generell vermeiden sollten (die Kopiersemantik ist verschraubt).
scoped_ptr: kein Overhead, kann nicht kopiert oder verschoben werden.
unique_ptr: kein Overhead, kann nicht kopiert werden, kann verschoben werden.
shared_ptr/ weak_ptr: Ein Teil des Overheads (Referenzzählung) kann kopiert werden.
Versuchen Sie normalerweise, entweder scoped_ptroder zu verwenden unique_ptr. Wenn Sie mehrere Eigentümer benötigen, versuchen Sie, das Design zu ändern. Wenn Sie das Design nicht ändern können und wirklich mehrere Eigentümer benötigen, verwenden Sie a shared_ptr, aber achten Sie auf Referenzzyklen, die mit a unterbrochen werden solltenweak_ptr irgendwo in der Mitte .
4. Intelligente Container
Viele intelligente Zeiger sind nicht zum Kopieren gedacht, daher ist ihre Verwendung mit den STL-Containern etwas beeinträchtigt.
shared_ptrVerwenden Sie intelligente Container aus dem Boost Pointer Container, anstatt auf und deren Overhead zurückzugreifen . Sie emulieren die Schnittstelle klassischer STL-Container, speichern jedoch Zeiger, die sie besitzen.
5. Rollen Sie Ihre eigenen
Es gibt Situationen, in denen Sie möglicherweise Ihren eigenen Smart Manager rollen möchten. Stellen Sie sicher, dass Sie nicht nur einige Funktionen in den Bibliotheken verpasst haben, die Sie zuvor verwendet haben.
Das Schreiben eines intelligenten Managers bei Ausnahmen ist ziemlich schwierig. Normalerweise können Sie nicht davon ausgehen, dass Speicher verfügbar ist ( newmöglicherweise fehlschlägt) oder dass Copy Constructordie no throwGarantie besteht.
Es kann einigermaßen akzeptabel sein, die std::bad_allocAusnahme zu ignorieren und aufzuerlegen, dass Copy Constructors einer Reihe von Helfern nicht fehlschlagen ... schließlich ist boost::shared_ptrdies der Grund für den DParameter zum Löschen von Vorlagen.
Aber ich würde es nicht empfehlen, besonders für Anfänger. Es ist ein heikles Problem, und Sie werden die Fehler im Moment wahrscheinlich nicht bemerken.
6. Beispiele
using namespace boost;
struct Cloneable
{
virtual ~Cloneable() {}
virtual Cloneable* clone() const = 0;
};
struct Derived: Cloneable
{
virtual Derived* clone() const { new Derived(*this); }
};
void scoped()
{
scoped_ptr<Cloneable> c(new Derived);
}
unique_ptr<Cloneable> unique()
{
return unique_ptr<Cloneable>(new Derived);
}
void shared()
{
shared_ptr<Cloneable> n1(new Derived);
weak_ptr<Cloneable> w = n1;
{
shared_ptr<Cloneable> n2 = n1;
n1.reset();
assert(n1.get() == 0);
assert(n2.get() != 0);
assert(!w.expired() && w.get() != 0);
}
assert(w.expired());
}
void container()
{
ptr_vector<Cloneable> vec;
vec.push_back(new Derived);
vec.push_back(new Derived);
vec.push_back(
vec.front().clone()
);
}