Frage

Wenn Sie die Pimpl Idiom ist es vorzuziehen, eine boost:shared_ptr anstelle eines std::auto_ptr zu benutzen? Ich bin sicher, ich habe einmal gelesen, dass die Boost-Version mehr Ausnahme freundlich?

ist
class Foo
{
public:
    Foo();
private:
    struct impl;
    std::auto_ptr<impl> impl_;
};

class Foo
{
public:
    Foo();
private:
    struct impl;
    boost::shared_ptr<impl> impl_;
};

[EDIT] Ist es immer sicher std zu verwenden :: auto_ptr <> oder gibt es Situationen, in denen ein alternativen Boost-Smart-Pointer erforderlich ist?

War es hilfreich?

Lösung

Sie sollten wirklich nicht std :: auto_ptr für diese. Der Destruktor wird an dem Punkt nicht sichtbar sein, erklären Sie die std :: auto_ptr, so dass es möglicherweise nicht richtig aufgerufen werden. Das setzt voraus, dass Sie sich auf Ihre Pimpl Klasse deklarieren und das Erstellen die Instanz innerhalb des Konstruktors in einer anderen Datei.

Wenn Sie :: scoped_ptr ( keine Notwendigkeit für shared_ptr hier, Sie werden nicht die Pimpl mit anderen Objekten teilen, und dies erzwungen wird durch scoped_ptr sein noncopyable ), müssen Sie nur die Pimpl destructor sichtbar an der Stelle, die scoped_ptr Konstruktor aufrufen.

z.

// in MyClass.h

class Pimpl;

class MyClass 
{ 
private:
    std::auto_ptr<Pimpl> pimpl;

public: 
    MyClass();
};

// Body of these functions in MyClass.cpp

Hier wird der Compiler den destructor von MyClass erzeugen. Welche auto_ptr Destruktor rufen müssen. An der Stelle, wo die auto_ptr destructor instanziert wird, ist Pimpl ein unvollständiger Typ. So in das auto_ptr Destruktor, wenn er die das Pimpl Objekt löscht, wird es nicht wissen, wie der Pimpl destructor nennen.

boost :: scoped_ptr (und shared_ptr) dieses Problem nicht haben, denn wenn man den Konstruktor eines scoped_ptr (oder die Reset-Methode) aufrufen, es macht auch eine Funktion-pointer-Äquivalent, dass es anstelle des Aufrufs löschen verwenden . Der entscheidende Punkt hier ist, dass es die Freigabe-Funktion instanziiert, wenn Pimpl nicht ein unvollständiger Typ ist. Als Randnotiz ermöglicht Shared_ptr Sie ein Deallokation Funktion, so dass Sie es für Dinge verwenden können, wie GDI-Handles oder was auch immer sonst können Sie wollen -. aber das ist übertrieben für Ihre Bedürfnisse hier

Wenn Sie wirklich std :: auto_ptr verwenden möchten, dann müssen Sie besonders darauf achten, indem sie sicher, dass Sie Ihre MyClass destructor in MyClass.cpp definieren, wenn Pimpl vollständig definiert ist.

// in MyClass.h

class Pimpl;

class MyClass 
{ 
private:
    std::auto_ptr<Pimpl> pimpl;

public: 
    MyClass();
    ~MyClass();
};

und

// in MyClass.cpp

#include "Pimpl.h"

MyClass::MyClass() : pimpl(new Pimpl(blah))
{
}

MyClass::~MyClass() 
{
    // this needs to be here, even when empty
}

Der Compiler den Code zu zerstören alle der MyClass Mitglieder effektiv ‚in‘ der leeren destructor erzeugen. Also an der Stelle wird der auto_ptr destructor instanziiert, Pimpl ist nicht mehr unvollständig und der Compiler nun weiß, wie der destructor nennen.

Ich persönlich glaube nicht, es lohnt sich die Mühe, dass alles machen richtig ist. Es gibt auch die Gefahr, dass jemand später zusammen kommen und den Code aufräumen, indem den scheinbar redundanten destructor entfernen. So ist es einfach sicherer rundum geht mit boost :: scoped_ptr für diese Art der Sache.

Andere Tipps

Ich neige dazu, auto_ptr zu verwenden. Seien Sie sicher, dass Ihre Klasse noncopyable (deklarieren private Kopie Ctor & operator =, oder erben boost::noncopyable) zu machen. Wenn Sie auto_ptr verwenden, eine Falte ist, dass Sie einen nicht-Inline-destructor definieren müssen, auch wenn der Körper leer ist. (Dies liegt daran, wenn Sie die Compiler lassen den Standard destructor erzeugen, wird impl ein unvollständiger Typ sein, wenn der Anruf an delete impl_ erzeugt wird, nicht definiertes Verhalten aufrufe).

Es gibt wenig zwischen auto_ptr & dem Boost-Zeiger zu wählen. Ich neige dazu, nicht Schub aus stilistischen Gründen zu verwenden, wenn eine Standardbibliothek Alternative tun wird.

Die Boost-Alternative zu std::auto_ptr ist boost::scoped_ptr. Der wesentliche Unterschied zu auto_ptr ist, dass boost::scoped_ptr ist noncopyable.

Siehe dieser Seite für weitere Details.

boost :: shared_ptr ist speziell zugeschnitten für Pimpl Idiom zu arbeiten. Einer der Hauptvorteile ist, dass es nicht zu definieren, den Destruktor für die Klasse Halt PIMPL ermöglicht. Gemeinsames Eigentum Politik vielleicht beides Vor- und Nachteile. Aber in späteren Fall können Sie Copykonstruktor richtig definieren.

Wenn Sie sind wirklich pedantisch es keine absolute Garantie dafür, dass ein auto_ptr Element verwendet keine vollständige Definition des Template-Parameter an der Stelle des auto_ptr erfordern, bei dem es verwendet wird. Having said that, habe ich noch nie nicht gesehen arbeiten.

Eine Variante ist ein const auto_ptr zu verwenden. Das funktioniert so lange, wie Sie Ihre ‚Pimpl‘ mit einem neuen Ausdruck in der initialiser Liste erstellen können und garantiert, dass der Compiler nicht default Copykonstruktor und Zuordnungsverfahren erzeugen. Ein Nicht-Inline-destructor für die umgebende Klasse muss noch zur Verfügung gestellt werden.

Andere Dinge gleich sind, würde ich eine Implementierung bevorzugen, die nur die Standardbibliotheken verwendet, wie es Dinge mehr tragbar hält.

Wenn Sie eine kopierbar Klasse möchten, verwenden Sie scoped_ptr, das Kopieren verbietet, damit Ihre Klasse schwer machen standardmäßig falsch zu verwenden (im Vergleich zur Verwendung shared_ptr, wird der Compiler nicht kopieren Einrichtungen auf eigene emittieren, und im Falle von shared_ptr wenn Sie nicht wissen, was Sie tun [was der Fall auch für Assistenten oft genug ist], gäbe es ein merkwürdiges Verhalten, wenn plötzlich eine Kopie von etwas ändert auch, dass etwas) und dann eine Kopie-Konstruktor aus definieren und kopieren -assignment:

class CopyableFoo {
public:
    ...
    CopyableFoo (const CopyableFoo&);
    CopyableFoo& operator= (const CopyableFoo&);
private:
    scoped_ptr<Impl> impl_;
};

...
CopyableFoo (const CopyableFoo& rhs)
    : impl_(new Impl (*rhs.impl_))
{}

shared_ptr ist viel besser für Pimpl Auto_ptr weil Ihre äußeren Klasse plötzlich seinen Zeiger enden könnte zu verlieren, wenn Sie es kopieren.

Mit Shared_ptr Sie Typ einen nach vorne erklärt, so dass Arbeiten nutzen können. auto_ptr nicht einen nach vorne deklarierten Typ ermöglichen. Auch nicht scoped_ptr und wenn Ihre äußeren Klasse als nicht kopierbar sowieso geht und hat nur einen Zeiger, kann es auch ein regelmäßiger sein.

Es gibt viel zu für die Verwendung eines intrusiven Referenzzähler im Pimpl gesagt werden, und die äußere Klasse erhalten ihre Kopie zu nennen und Semantik in ihrer Umsetzung zu. Angenommen, dies ist ein echter Anbieter (liefert die Klasse) Modell, das es besser ist der Verkäufer die Benutzer nicht zwingen Shared_ptr zu sein, oder, um die gleiche Version von shared_ptr mit (Boost oder std).

Ich habe wirklich glücklich über impl_ptr von Vladimir Batov [geändert] . Es macht es wirklich einfach, eine Pimpl zu erstellen, ohne dass expliziten Kopie-Konstruktor und Zuweisungsoperator machen.

Ich habe den ursprünglichen Code geändert, so dass es jetzt eine shared_ptr ähnelt, so dass es in Epilog-Code verwendet werden kann, und bleibt schnell.

Versuchen Sie nicht so schwer, sich in den Fuß zu schießen, in C ++ Sie viele Möglichkeiten haben :) Es gibt keine wirkliche Notwendigkeit, entweder automatisch Zeiger zu verwenden, da Sie perfekt wissen, wann Ihr Objekt geht in und aus dem Leben (in Ihrem Konstruktor (en) und destructor).

Halten Sie es einfach.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top