Hier ist ein Spielzeugbeispiel, das ein Problem darstellt, dem ich begegne. Die Anwendung ist ziemlich irrelevant (es ist im Wesentlichen eine verkettete Liste von Elementen mit einem speziellen Verhalten am Ende). Ich bin nicht in der Lage, eine Basisklasse shared_ptr mit einem abgeleiteten Zeiger zu konstruieren und es ist aus irgendeinem Grund mit der Tatsache verbunden, dass ich private Vererbung verwende.
%Vor%und ich bekomme das
%Vor%Ich hatte den Eindruck, dass die private Vererbung der abgeleiteten Klasse den Zugriff auf die öffentliche Schnittstelle der Basisklasse ermöglicht, diese Schnittstelle jedoch für externe Clients ausblendet. Warum verursacht private Vererbung diesen Fehler (funktioniert es, wenn ich öffentlich erben)?
Ändern Sie diese einzelne Zeile:
%Vor%bis
%Vor%Und es kompiliert.
Oder zumindest bei Verwendung der std
-Bibliothek. In der Regel ist dies für boost
ähnlich.
Es gibt noch andere Fehler, aber das führt dazu, dass B*
in A*
umgewandelt wird.
Warum funktioniert das? Weil die Vorlage für den Konstruktor std::shared_ptr<A>
nicht das ist, was du denkst! Es ist eher wie template <class X> std::shared_ptr(X* v)
. So wird die tatsächliche B*
to A*
Besetzung verschoben und in einem Nicht-Freund Mitglied fehlgeschlagen.
Aber wenn Sie den B*
-Zeiger (dh this
) auf A*
innerhalb einer Methode von class B
(der einzige Ort, der legal ist ohne eine friend
-Deklaration) setzen, sind Sie drin.
NB: Mit der privaten Erbschaft ist grundsätzlich nichts falsch. Es ist kein Anti-Pattern und wird aus gutem Grund zur Verfügung gestellt. Denken Sie über Komposition nach, aber Objekte, die einigen Teilen der Anwendung verbieten, auf ihren "echten" Typ zuzugreifen, haben eine Menge Nutzen. Geben Sie beispielsweise ein Objekt A aus, das über einige B-Bolzen verfügt, auf die nur die Objektfactory zugreifen kann.
PS: Der Grund, warum der Konstruktor template<class T> shared_ptr<T* v>
ist, ist, dass shared_ptr
den Deleter des Typs verwendet, der an ihn übergeben wurde.
Wie Sie sicher nicht wissen, ruft share_ptr
geschickt den 'richtigen' Destruktor auf, auch wenn es nicht virtuell ist.
Mein "Fix" untergräbt diese Klugheit, also hüte dich davor, den richtigen Deleter zu übergeben, oder (empfohlen) mache den Destruktor von A
virtuell.
PPS:
Und schließlich ein voll funktionsfähiges Programm (mit STL. Tut mir leid, ich habe keine Boost):
%Vor% Sie müssen enable_shared_from_this
verwenden, weil Sie sonst versuchen, zwei 'Familien' von shared_ptr
zu erstellen, und das wird nicht funktionieren.
Ich habe eine Factory-Methode gemacht, weil das Fixieren des Konstruktors einfach nicht funktionieren würde!
Es gibt eine Vorbedingung von enable_shared_from_this
, dass es ein std::shared_ptr
geben muss, und ich nehme an, dass dies "vollständig konstruiert" bedeutet.
Der folgende Konstruktor funktioniert nicht für mich:
%Vor% Das heißt, wenn Sie von enable_shared_from_this
erben, ist es eine gute Idee, alle Konstruktoren privat zu machen und Fabriken bereitzustellen, die shared_ptr
zurückgeben. Andernfalls können Sie in eine richtige Sauerei geraten, wenn der Aufrufcode nicht selbst dafür sorgt, dass die 'existing shared_ptr
' Bedingung ist. Ein fieses Stück Kupplung, wenn es jemals einen gab.
Wenn Sie private Vererbung verwenden, sagen Sie im Grunde: "Ich möchte, dass B in A implementiert wird, aber ich möchte nicht, dass es wie ein A (is-a A) verwendet wird"
Hier geben Sie boost::shared_ptr
einen Zeiger auf B, als ob es ein A wäre.
Das ist ein Widerspruch in Ihrem Design. Vielleicht würde boost::shared_ptr<A>
einen Freund von B deklarieren, aber es ist immer noch ein komisches Design.
Zusätzliche Anmerkung: Wenn Sie möchten, dass B ein spezielles A ist, das die Benutzeroberfläche von A nicht freigibt, sollten Sie die Komposition anstelle der privaten Vererbung in Betracht ziehen.
Tags und Links c++ inheritance shared-ptr