Warum sollte ich in dieser Situation einen intelligenten Zeiger verwenden?

8

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
  • instanziiert werden
  • Das Objekt, auf das verwiesen wird, kann gelöscht werden / den Geltungsbereich verlassen, ohne dass 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.

    
user463035818 04.05.2015, 13:38
quelle

5 Antworten

1

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.

    
Cássio Renan 04.05.2015, 14:10
quelle
2

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.

    
TartanLlama 04.05.2015 13:45
quelle
1

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.

    
Bill Lynch 04.05.2015 13:44
quelle
1

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

verwenden %Vor%

statt

%Vor%

Dann verwenden Sie als privates Zeigerelement Ihres SomeUtilityClass einen schwachen Zeiger:

%Vor%

und initialisiere es mit shared_ptr :

%Vor%

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:

%Vor%

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.

    
Wojtek Surowka 04.05.2015 13:55
quelle
0

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:

  1. Sie müssen jedes Mal nach NULLs suchen, wenn Sie über den Smart Pointer auf das Objekt zugreifen.
  2. Wenn ein Smart-Zeiger auf eine Instanz einer Klasse zeigt, sagen wir 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.
Sergey Tachenov 04.05.2015 14:03
quelle

Tags und Links