2016-03-28 2 views
2

Obwohl mein Code gut kompiliert ist, hat mich das etwas gestört, und ich konnte keine Antwort auf Stackoverflow finden. Der folgende generische Konstruktor ist eine Möglichkeit, eine shared_ptr an eine Klasseninstanz im Konstruktor zu übergeben.Warum wird beim Kopieren eines const shared_ptr & const-ness nicht verletzt?

MyClass { 
    MyClass(const std::shared_ptr<const T>& pt); 
    std::shared_ptr<const T> pt_; //EDITED: Removed & typo 
}; 

MyClass::MyClass(const std::shared_ptr<const T>& pt) 
    : pt_(pt) 
{ } 

Dies kompiliert gut. Meine Frage ist die folgende: In meinem Verständnis, einen Parameter const wie folgt deklarieren:

void myfunc(const T& t) 

verspricht, nicht t zu ändern. Kopiere ich jedoch die shared_ptr pt zu pt_, steige ich nicht effektiv auf die Verwendungszählung von shared_ptr pt und verletze damit die angenommene const-ness?

Dies könnte ein grundlegendes Missverständnis von shared_ptrs auf meiner Seite sein?

(Für alle, die dies lesen, sie umzusetzen suchen, beachten Sie, dass this könnte eine bessere Umsetzung sein)

+0

@LogicStuff: Wenn du extra und in der Klassendefinition meintest, habe ich meinen Beitrag bearbeitet. Aber die Konstruktordefinition kompiliert gut? – Thomas

+1

@Thomas Nun, da es ein 'const' Objekt nicht an einen Verweis-zu-nicht-const bindet, ja. – LogicStuff

+0

@MichaelBurr bearbeitet, ich schätze, du hast dich auch auf meinen Tippfehler in der Deklaration von pt_ bezogen, tut mir leid. – Thomas

Antwort

4

Eines der Mitglieder, dass std::shared_prt<> haben muss, ist die altmodische Copykonstruktor:

shared_ptr(const shared_ptr& r) noexcept; 

Der Standard sagt (C++ 11 20.7.2.2.1/18 "Shared_ptr Bauer"), dass „wenn r ist leer, erstellt ein leeres shared_ptr-Objekt; andernfalls wird ein shared_ptr-Objekt erstellt, das den Eigentümer mit r teilt.

Der Standard erwähnt nicht, wie "Aktienbesitz mit r" durch eine const Referenz erreicht werden kann. Einige Optionen könnten:

  • die privaten Mitglieder, das Eigentum Semantik geteilt implementieren könnte mutable
  • die Datenstrukturen gekennzeichnet sein, dass das Eigentum geteilt Arbeitsgerät möglicherweise nicht tatsächlich in dem shared_ptr Objekt lebt - sie könnten eine separate Gruppe von Objekten sein das könnte zum Beispiel durch einen Zeiger erhalten werden.
+0

Upvote für die Erwähnung des Schlüsselwortes * mutable *, das ich in meiner beschränkten Exposition gegenüber C++ noch nicht kennengelernt hatte! – Thomas

1

Meine Frage ist folgende: In meinem Verständnis, einen Parameter const wie folgt erklärt ... verspricht nicht t ändern.

Nicht ganz richtig. Das Versprechen ist nicht, irgendeinen beobachtbaren Zustand zu ändern ... die meiste Zeit. Es gibt mehrere Möglichkeiten, in denen ein konstantes Objekt „ändern“ kann:

  1. Es änderbare Variablen hat - diese sollen unter const Einschränkungen ändern, aber Design-Methodik, sagt diese selten sein sollte und nicht beobachtbar sein. Eine häufigere Verwendung für sie ist das Zwischenspeichern von etwas, das teuer zu berechnen ist. Sie haben also eine const-Funktion get, die eine massive Berechnung ausführt, um einen Wert zurückzugeben - Sie möchten, dass dies optimiert wird, damit Sie einen Cache erstellen. Der Cache muss sich während des Aufrufs get ändern, aber in Wirklichkeit gibt get immer dasselbe zurück, so dass niemand beobachten konnte, dass sich der Status des Objekts geändert hat.

  2. Es hat nicht-const Zeiger oder Verweise auf andere Objekte. In diesen Fällen, der Aggregation, ändert sich nicht das Objekt, sondern etwas anderes. Dies geschieht im Fall von shared_ptr, das einen Zeiger auf ein geteiltes Referenzzählobjekt hat, das den Wert des Zeigers tatsächlich enthält. Es ist anfangs nicht intuitiv, da sich der gemeldete Zustand eines solchen Objekts ändern kann, aber es ist tatsächlich nicht das Objekt selbst, das sich geändert hat. Die Designmethodik wird hier von Fall zu Fall festgelegt, aber die Sprache schützt Sie in keiner Weise, es sei denn, Sie deklarieren die Zeiger als Zeiger auf const.

+0

Ich denke, Punkt 2. beantwortet meine Frage, danke! Um es noch einmal zu wiederholen, um sicherzustellen, dass ich es richtig verstehe: Mein shared_ptr pt ist wie eine Instanz einer Klasse, und die Klasse enthält ein (nicht-konstantes!) Ptr, zum Beispiel eine Ganzzahl, die Referenzen zählt? Also gibt die Funktion shared_ptr.use_count() nicht tatsächlich ein Mitglied von pt selbst zurück, sondern gibt diese Ganzzahl zurück? – Thomas

+1

Ja, nah genug. 'shared_ptr' ist eigentlich ziemlich dumm an und für sich. Die meisten Smarts befinden sich in einem nicht spezifizierten Objekt, das die Referenzzählung und den Zeiger enthält. Wenn der letzte 'shared_ptr' den Gültigkeitsbereich verlässt, wird der Verweiszähler auf 0 gesetzt und das Objekt wird bereinigt. Kopien von 'shared_ptr'-Instanzen erhöhen nur die Anzahl und kopieren einen Verweis darauf. Ich bin mir sicher, dass auch andere Sachen dazu beitragen, die Leistung und so weiter zu verbessern ... aber das ist das Wesentliche, soweit es uns hier betrifft. –

1

Geben eines shared_ptr als Referenz seiner nicht Bezug count.Moreover erhöhen, Sie kopieren alles hier nicht, man muss nur Referenzen nehmen, so dass die Referenzzahl unverändert bleibt.

Beachten Sie, dass die Verwendung eines Verweises auf einen gemeinsamen Zeiger als Klassenmitglied in der Regel nicht das ist, was Sie wollten. Dadurch erhalten Sie keine Garantie, dass der Zeiger noch aktiv ist, wenn Sie ihn verwenden möchten. Dies ist im Grunde das Ziel, wenn Sie einen geteilten Zeiger verwenden.

Antwort auf Ihre Bearbeitung: Jetzt, mit einem Shared-Pointer-Mitglied, erstellen Sie in der Tat eine Kopie und erhöhen somit die ref-count. Dies ist möglich, da Sie konstante Objekte kopieren können.So existiert diese von Ihnen erwähnte Garantie nicht - im Wesentlichen wird das Schlüsselwort mutable davon abgehalten.

+0

Wie ist das relevant? Die Frage ist über Zeiger auf const, nicht const Zeiger ... – Amit

+0

@Amit: die Frage ist über die Referenzzählung des shared-pointer ... die ich in meiner Frage beantworte (--aber die * Sie * scheinen zu vermissen in deiner Antwort). – davidhigh

+0

Sie haben absolut recht. Ich wurde von der Fülle von Const's weggeworfen :-) – Amit

2

const, bedeutet in einer Schnittstelle, was immer es bedeuten will. Seine Bedeutung sollte dokumentiert werden.

Normalerweise bedeutet es "eine Teilmenge von ny Zustand wird sich nicht ändern". Bei einem freigegebenen ptr ist die Teilmenge des Zustands, die nicht geändert werden kann, "worauf ich hinweise".

Die Anzahl kann sich ändern. Der Inhalt kann sich ändern.

in der STD-Bibliothek C++, const kann "thread-safe" so interpretiert werden - denn wenn const Operationen Thread-sicher sind, und Sie setzen sie in std Container, Thread-sichere konst Operationen der std Behälter wiederum hat.

Mit thread sicher, ich meine nicht sync - ich meine zwei verschiedene threads, beide tun const stuff, ist a-ok. Wenn ein Thread nicht-konstante Sachen macht, sind alle Wetten deaktiviert.

Dies ermöglicht eine einfache Leser-Schreiber-Verriegelungslogik.

Und als Add/Remove ref tatsächlich sicher ist, fädelt, während reseating ptr

nicht ... ist
3

Die einfache Antwort auf Ihre Frage ist keine, weil der Referenzzähler in der freigegebenen Zeiger Instanz nicht gespeichert , aber in einem externen Objekt, das dafür sorgt, dass die Referenzzählung erhalten bleibt. Wenn Sie ein shared_ptr-Konstrukt kopieren, wird die Referenzzählung im externen Objekt hinzugefügt. Check-out this lecture von Stephen T. Lavavej welches die Umsetzung

1

A erklärt geteilt Zeiger wird konzeptuell angelegt, wie folgt:

Der Shared_ptr enthält einen Zeiger zu dem Objekt und auch ein Zeiger auf einen Steuerblock. Es ist der Steuerblock, der die Lebensdauer des Verweises steuert, nicht das Objekt shared_ptr selbst, das nicht mehr als ein Wrapper und ein Code ist, um den Steuerblock zu benachrichtigen, dass die Anzahl der Referenzen erhöht oder verringert wurde. Es ist der Steuerblock, der den Referenzzählwert, den Löschvorgang und den Zeiger auf die ursprüngliche Schnittstelle des Pointees speichert (so kann der Deletor gegen die richtige Schnittstelle löschen, selbst wenn ein Zeiger gewirkt wurde).

* shared_ptr object * 
| pointer to object | ---------------> object 
| pointer to control block |----+ +> (possibly different interface 
           | | but still same object) 
           | | 
* control block * <----------+ | 
| reference count |    | 
| deleter   |    | 
| pointer to object | --------------+ 

Da ein shared_ptr Speicher etwas wie folgt aussieht:

template<class T> 
struct shared_ptr { 
    T* ptr; 
    control_block* pctrl; 
}; 

Es beginnen sollte klar werden, dass selbst wenn die Shared_ptr const ist, erfordert internen Zustand jede Mutation der shared_ptr ist keine Kopie unter . Die Mutation passiert im Steuerblock, der auf von shared_ptr zeigt.

So wird der Vertrag nicht gebrochen. So wie wenn Sie

erklärt
T* const p; 

p Modifizierung selbst ist nicht möglich, aber (*p) Modifizierung ist durchaus sinnvoll.

Verwandte Themen