Was ist die beste Methode, um shared_ptr
einen abgeleiteten Typ an eine Funktion zu übergeben, die shared_ptr
einen Basistyp verwendet?
Ich übergebe im Allgemeinen shared_ptr
s als Referenz, um eine unnötige Kopie zu vermeiden:
int foo(const shared_ptr<bar>& ptr);
aber das funktioniert nicht, wenn ich versuche, so etwas zu tun
int foo(const shared_ptr<Base>& ptr);
...
shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
ich könnte benutzen
foo(dynamic_pointer_cast<Base, Derived>(bar));
Dies scheint jedoch aus zwei Gründen nicht optimal zu sein:
- A
dynamic_cast
scheint für eine einfache Ableitung von der Basis zur Basis etwas übertrieben zu sein. - So wie ich es verstehe,
dynamic_pointer_cast
wird eine Kopie (wenn auch eine temporäre) des Zeigers erstellt, die an die Funktion übergeben wird.
Gibt es eine bessere Lösung?
Update für die Nachwelt:
Es stellte sich heraus, dass eine Header-Datei fehlte. Auch was ich hier versucht habe, wird als Antimuster angesehen. Allgemein,
Funktionen, die sich nicht auf die Lebensdauer eines Objekts auswirken (dh das Objekt bleibt für die Dauer der Funktion gültig), sollten eine einfache Referenz oder einen Zeiger verwenden, z
int foo(bar& b)
.Funktionen, die ein Objekt verbrauchen (dh die Endbenutzer eines bestimmten Objekts sind), sollten einen
unique_ptr
By-Wert annehmen , zint foo(unique_ptr<bar> b)
. Anrufer solltenstd::move
den Wert in die Funktion einfließen lassen .Funktionen, die die Lebensdauer eines Objekts verlängern, sollten einen
shared_ptr
By-Wert annehmen , zint foo(shared_ptr<bar> b)
. Die übliche Beratung zu vermeiden zirkuläre Referenzen gilt.
Weitere Informationen finden Sie im Back to Basics-Vortrag von Herb Sutter .
quelle
shared_ptr
? Warum keine konstante Referenz von bar?dynamic
Besetzung wird nur zum Downcasting benötigt. Außerdem sollte das Übergeben des abgeleiteten Zeigers einwandfrei funktionieren. Es wird ein neuesshared_ptr
mit demselben Refcount erstellt (und erhöht) und ein Zeiger auf die Basis, der dann an die const-Referenz gebunden wird. Da Sie jedoch bereits eine Referenz nehmen, verstehe ich nicht, warum Sie überhaupt eine nehmen möchtenshared_ptr
. Nehmen Sie einBase const&
und rufen Sie anfoo(*bar)
.foo(bar)
) funktioniert zumindest in MSVC 2010 nicht.shared_ptr
, das an die Funktion übergeben wird? Ich bin mir ziemlich sicher, dass es keinen Weg gibt, das zu vermeiden.Antworten:
Obwohl
Base
undDerived
sind kovariant und rohe Zeiger auf sie werden entsprechend handelnshared_ptr<Base>
undshared_ptr<Derived>
sind nicht kovariant. Dasdynamic_pointer_cast
ist der richtige und einfachste Weg , um dieses Problem zu behandeln.( Bearbeiten:
static_pointer_cast
wäre besser geeignet, da Sie von abgeleitet zu Basis übertragen, was sicher ist und keine Laufzeitprüfungen erfordert. Siehe Kommentare unten.)Wenn Ihre
foo()
Funktion jedoch nicht an der Verlängerung der Lebensdauer teilnehmen möchte (oder vielmehr am gemeinsamen Besitz des Objekts teilnehmen möchte), ist es am besten, a zu akzeptierenconst Base&
und das zu dereferenzieren,shared_ptr
wenn Sie es an übergebenfoo()
.Da
shared_ptr
Typen nicht kovariant sein können, gelten die Regeln für implizite Konvertierungen über kovariante Rückgabetypen hinweg nicht, wenn Typen von zurückgegeben werdenshared_ptr<T>
.quelle
shared_ptr<Derived>
implizit in konvertiert werdenshared_ptr<Base>
, sodass der Code ohne Casting-Spielereien funktionieren sollte.shared_ptr<Ty>
hat einen Konstruktor, der a nimmtshared_ptr<Other>
und die entsprechende Konvertierung durchführt, wenn erTy*
implizit in konvertierbar istOther*
. Und wenn eine Besetzung benötigt wird,static_pointer_cast
ist hier die passende nichtdynamic_pointer_cast
.shared_ptr
um die Referenzzählung zu vermeiden, gibt es wirklich keinen guten Grund, überhaupt a zu verwendenshared_ptr
. Es ist am besten,const Base&
stattdessen zu verwenden .static_pointer_cast
, danke.Dies geschieht auch, wenn Sie vergessen haben, die öffentliche Vererbung für die abgeleitete Klasse anzugeben , dh wenn Sie wie ich Folgendes schreiben:
quelle
class
ist für Vorlagenparameter;struct
dient zum Definieren von Klassen. (Dies ist höchstens 45% ein Witz.)Klingt so, als würden Sie sich zu sehr anstrengen.
shared_ptr
ist billig zu kopieren; Das ist eines seiner Ziele. Wenn Sie sie als Referenz weitergeben, wird nicht wirklich viel erreicht. Wenn Sie keine Freigabe wünschen, übergeben Sie den Rohzeiger.Es gibt jedoch zwei Möglichkeiten, die ich mir auf den ersten Blick vorstellen kann:
quelle
shared_ptr
Implementierung geschrieben, die Microsoft liefert.Überprüfen Sie auch, ob sich die
#include
Header-Datei mit der vollständigen Deklaration der abgeleiteten Klasse in Ihrer Quelldatei befindet.Ich hatte dieses Problem. Das
std::shared<derived>
würde nicht besetzenstd::shared<base>
. Ich hatte beide Klassen vorwärts deklariert, damit ich Zeiger auf sie halten konnte, aber weil ich den nicht hatte, konnte#include
der Compiler nicht sehen, dass eine Klasse von der anderen abgeleitet war.quelle