Ich habe nie irgendeinen intelligenten Zeiger benutzt, aber ich lese immer über sie, wenn das Thema Zeiger ist. Ich verstehe, dass es Situationen gibt, in denen intelligente Zeiger viel netter sind als rohe Zeiger, weil sie in gewissem Umfang den Besitz des Zeigers verwalten. Ich weiß jedoch immer noch nicht, wo die Grenze zwischen "Ich brauche keine intelligenten Zeiger dafür" und "Dies ist ein Fall für intelligente Zeiger" liegt.
Sagen wir, ich habe folgende Situation:
%Vor% Dies ist natürlich ein vereinfachtes Beispiel. Was ich meine ist, dass SomeUtilityClass
eine Instanz von A
nicht "besitzen" sollte (weil es nur eine Utility-Klasse ist), also hält es einfach einen Zeiger.
Was den Zeiger betrifft, ist das einzige, was mir bewusst ist, dass es schief gehen könnte:
SomeUtilityClass
kann mit einem Null-Zeiger SomeUtilityClass
es bemerkt Wie könnte ein intelligenter Zeiger helfen, dieses Problem zu vermeiden? Welche anderen Vorteile würde ich mit einem intelligenten Zeiger in diesem Fall erhalten?
PS: Ich weiß, dass es mehrere Fragen zu intelligenten Zeigern gibt (zB dieses ). Ich würde mich jedoch freuen, wenn Sie mir von den Auswirkungen auf dieses spezielle Beispiel erzählen könnten.
Für die Zwecke dieser Antwort definiere ich setA wie folgt:
%Vor%Überlegen Sie:
%Vor% Nachdem der Bereich abgeschlossen ist, hat SomeUtilityClass
einen freien Zeiger und getResult()
ruft Undefined Behavior auf. Beachten Sie, dass dies nicht mit einer Referenz gelöst werden kann: Sie würden immer noch einen baumelnden bekommen.
Betrachten Sie nun die Version mit einem intelligenten Zeiger:
%Vor% Da Sie das Eigentumsrecht geteilt haben, gibt es keine Möglichkeit, einen ungeeigneten Zeiger zu erhalten. Der Speicher, auf den b
zeigt, wird wie üblich gelöscht, aber nur, nachdem u
zerstört wurde (oder der Zeiger geändert wurde).
IMHO, in den meisten Fällen sollten Sie Smartpointer verwenden (auch wenn es zunächst nicht viel Sinn macht). Es erleichtert die Wartung erheblich. Verwenden Sie rohe Zeiger nur in einem bestimmten Code, der sie tatsächlich benötigt, und kapseln Sie diesen Code so weit wie möglich ein.
Dies hängt davon ab, wie der Parameter erstellt und gespeichert wird. Wenn Sie den Speicher nicht besitzen und dieser entweder statisch oder dynamisch zugewiesen werden kann, ist ein roher Zeiger eine durchaus sinnvolle Lösung - besonders, wenn Sie wie in Ihrem Beispiel das Austauschen der Daten unterstützen müssen. Eine andere Option wäre, std::reference_wrapper
zu verwenden, was das Problem nullptr
bei gleicher Semantik beseitigen würde.
Wenn Sie einen Zeiger auf eine freigegebene Ressource halten (d. h. irgendwo in einer std::shared_ptr
gespeichert) und prüfen möchten, ob sie gelöscht wurde oder nicht, können Sie std::weak_ptr
speichern.
Wenn SomeUtilityClass
nicht die Membervariable a
besitzt, dann ist ein Smart Pointer nicht sinnvoll.
Sie könnten ein Referenzelement in Betracht ziehen, das die Probleme eines Nullzeigers beseitigen würde.
Die Standardmethode zum Ausdrücken eines nicht besitzenden Zeigers in C ++ ist weak_ptr
. Um weak_ptr
verwenden zu können, müssen Sie shared_ptr
für den Besitz verwenden, also würden Sie in Ihrem Beispiel
statt
%Vor% Dann verwenden Sie als privates Zeigerelement Ihres SomeUtilityClass
einen schwachen Zeiger:
und initialisiere es mit shared_ptr
:
Sie können jedoch weak_ptr
nicht direkt verwenden, da shared_ptr
den Gültigkeitsbereich verlassen könnte und Ihr schwacher Zeiger nicht mehr auf etwas zeigen kann. Vor der Verwendung müssen Sie es sperren:
Der Zeiger locked
ist leer, wenn der besitzende Zeiger ein Objekt nicht länger verwaltet, da z.B. es ging aus dem Rahmen. Wenn es nicht leer ist, können Sie es verwenden und es wird dann den Gültigkeitsbereich des Objekts automatisch verlassen.
Sowohl shared_ptr
als auch weak_ptr
sind in der Standardbibliothek in C ++ 11 und in Boost für ältere Compiler verfügbar.
Es gibt verschiedene Arten von intelligenten Zeigern. In Ihrem Fall ist klar, dass ein intelligenter Zeiger nicht wirklich benötigt wird, aber er kann dennoch einige Vorteile bieten.
SomeUtilityClass kann mit einem Null-Zeiger
instanziiert werden
Dies ist wahrscheinlich am besten mit einer Überprüfung im Konstruktor zu lösen, die eine Ausnahme auslöst oder auf andere Weise einen Fehler anzeigt, wenn Sie einen NULL-Zeiger als Argument erhalten. Ich kann mir kaum vorstellen, wie ein intelligenter Zeiger helfen würde, es sei denn, Sie verwenden eine bestimmte Smart Pointer-Klasse, die NULLs nicht akzeptiert, so dass es die Überprüfung für Sie bereits durchführt.
Das Objekt, auf das gezeigt wird, kann gelöscht / aus dem Gültigkeitsbereich herausgenommen werden, ohne das SomeUtilityClass bemerkt es
Dieser kann tatsächlich mit einem speziellen Typ von intelligenten Zeigern aufgelöst werden, aber dann ist es erforderlich, dass das Objekt, auf das gezeigt wird, irgendwie die Benachrichtigung über die Zerstörung unterstützt. Ein Beispiel dafür ist die QPointer
-Klasse in der Qt-Bibliothek, die nur auf QObject
-Instanzen verweisen kann, die sie beim Löschen benachrichtigen, sodass der Smart-Pointer automatisch NULL wird, wenn das Objekt gelöscht wird. Es gibt jedoch einige Probleme mit diesem Ansatz:
MyClass
, erweitern die Klasse, die die Löschbenachrichtigung durchführt ( QObject
im Qt-Fall), erhalten Sie seltsame Ergebnisse: Das ist der Destruktor von QObject, der den Smart Pointer benachrichtigt, so dass Sie darauf zugreifen können, wenn der MyClass
Destruktor bereits seine Drecksarbeit begonnen hat, also das Objekt teilweise zerstört ist, aber der Zeiger noch nicht NULL ist, weil die Destruktion noch nicht abgeschlossen ist Fortschritt. Tags und Links c++ pointers smart-pointers