Frage

    

Diese Frage bereits eine Antwort hier:

         

Backgrounder:

Die PIMPL Idiom (Zeiger auf Implementierung) ist eine Technik zur Durchführung Versteck, in dem eine öffentliche Klasse umschließt eine Struktur oder Klasse, die nicht außerhalb der Bibliothek zu sehen ist die öffentliche Klasse gehört.

Dies verbirgt interne Implementierungsdetails und Daten vom Benutzer der Bibliothek.

Wenn dieses Idiom Umsetzung warum würden Sie die öffentlichen Methoden auf der Pimpl Klasse platzieren und nicht die öffentliche Klasse, da die öffentlichen Klassen Methodenimplementierungen in die Bibliothek kompiliert werden würde, und der Benutzer hat nur die Header-Datei?

Zur Veranschaulichung stellt dieser Code die Purr() Implementierung auf der impl Klasse und wickelt es auch.

Warum implementieren nicht Purr direkt auf der öffentlichen Klasse?

// header file:
class Cat {
    private:
        class CatImpl;  // Not defined here
        CatImpl *cat_;  // Handle

    public:
        Cat();            // Constructor
        ~Cat();           // Destructor
        // Other operations...
        Purr();
};


// CPP file:
#include "cat.h"

class Cat::CatImpl {
    Purr();
...     // The actual implementation can be anything
};

Cat::Cat() {
    cat_ = new CatImpl;
}

Cat::~Cat() {
    delete cat_;
}

Cat::Purr(){ cat_->Purr(); }
CatImpl::Purr(){
   printf("purrrrrr");
}
War es hilfreich?

Lösung

  • Weil Sie Purr() wollen private Mitglieder von CatImpl nutzen zu können. Cat::Purr() würde einen solchen Zugang ohne friend Erklärung nicht erlaubt werden.
  • Weil Sie dann Verantwortung nicht mischen: eine Klasse implementiert, eine Klasse nach vorn.

Andere Tipps

Ich glaube, die meisten Menschen dies als Griffkörper Idiom beziehen. Sehen Sie James Coplien Buch erweiterte C ++ Programmierung Stile und Idiome ( Amazon Link ). Es ist auch bekannt als das Cheshire Cat wegen Lewis Caroll Charakter, das weg, bis nur noch das Grinsen bleibt verblasst .

Der Beispielcode sollte über zwei Sätze von Quelldateien verteilt werden. Dann nur Cat.h ist die Datei, die mit dem Produkt geliefert wird.

CatImpl.h durch Cat.cpp enthalten und CatImpl.cpp enthält die Implementierung für CatImpl :: Purr (). Dies wird nicht an die Öffentlichkeit mit Ihrem Produkt sichtbar sein.

Grundsätzlich ist die Idee ist, so viel wie möglich von der Umsetzung vor neugierigen Blicken zu verbergen. Dies ist besonders nützlich, wenn Sie ein kommerzielles Produkt, das als eine Reihe von Bibliotheken ausgeliefert, die über eine API zugegriffen werden, dass der Code des Kunden zusammengestellt gegen und verknüpft.

Wir haben dies mit dem Umschreiben von Ionaş Orbix 3.3 Produkt im Jahr 2000.

Wie bereits von anderen erwähnt, seine Technik vollständig die Implementierung von der Schnittstelle des Objekts abkoppelt. Dann werden Sie nicht neu kompilieren alles, was Cat verwendet, wenn Sie nur die Implementierung von Purr ändern möchten ().

Diese Technik in einer Methode verwendet wird, genannt Design by Contract .

Für das, was wert ist, ist es, die Umsetzung von der Schnittstelle trennt. Dies ist in der Regel nicht sehr wichtig, in der kleinen Größe Projekten. Aber in großen Projekten und Bibliotheken, kann es verwendet werden, um die Bauzeiten erheblich zu reduzieren.

Beachten Sie, dass die Umsetzung der Cat viele Header enthalten können, beinhalten kann Vorlage Meta-Programmierung, die Zeit auf seine eigene kompilieren dauert. Warum sollte ein Benutzer, der will nur die Cat verwenden, um das alles enthalten? Daher werden alle notwendigen Dateien versteckt sind die Pimpl Idiom mit (daher der Vorwärts-Erklärung CatImpl) und die Schnittstelle zwingen den Benutzer nicht, sie zu schließen.

Ich bin eine Bibliothek für nichtlineare Optimierung zu entwickeln ( „viele böse math“ lesen), die in Vorlagen implementiert, so dass die meisten der Code in Header ist. Es dauert etwa fünf Minuten zu kompilieren (auf einem anständigen CPU Multi-Core) und nur in einem sonst leeren .cpp dauert etwa eine Minute auf die Header-Parsing. So jemand die Bibliothek hat, um ein paar Minuten warten, jedes Mal wenn sie ihren Code zu kompilieren, die die Entwicklung macht ganz langweilig . Wenn jedoch die Umsetzung und die Überschriften versteckt, man enthält nur eine einfache Interface-Datei, die sofort kompiliert wird.

Es muss nicht unbedingt etwas aus mit dem Schutz der Umsetzung zu tun, die von anderen Unternehmen kopiert werden - die ohnehin nicht wohl passieren würde, es sei denn, das Innenleben Ihres Algorithmus aus den Definitionen der Membervariablen (wenn ja erraten werden , ist es wahrscheinlich nicht sehr kompliziert ist und nicht wert, in erster Linie den Schutz).

Wenn Ihre Klasse des Pimpl Idiom verwendet, können Sie die Header-Datei auf der öffentlichen Klasse vermeiden zu ändern.

Auf diese Weise können Sie Methoden, um die Pimpl Klasse hinzufügen / entfernen, ohne die externen Klasse-Header-Datei zu ändern. Sie können auch zu #includes zum Pimpl hinzufügen / entfernen.

Wenn Sie die externe Klasse-Header-Datei zu ändern, müssen Sie alles neu kompilieren, die es #include (und wenn einer von denen, Header-Dateien sind, haben Sie alles neu kompilieren, die sie #include, und so weiter)

Normalerweise wäre der einzige Hinweis auf Pimpl Klasse im Header für den Besitzer Klasse (Cat in diesem Fall) eine Vorwärts-Erklärung, wie Sie hier getan haben, denn das ist sehr um die Abhängigkeiten zu reduzieren.

Zum Beispiel, wenn Ihre Pimpl Klasse ComplicatedClass als Mitglied hat (und nicht nur einen Zeiger oder Verweis auf sie), dann müssen Sie ComplicatedClass vollständig definiert haben, bevor es Gebrauch ist. In der Praxis bedeutet dies, einschließlich „ComplicatedClass.h“ (zu denen auch indirekt etwas werden ComplicatedClass abhängt). Dies kann zu einer einzigen Kopffüllung führt in vielen, vielen Sachen ziehen, die für die Verwaltung Ihre Abhängigkeiten schlecht ist (und Ihre Kompilierung mal).

Wenn Sie die Pimpl idion verwenden, müssen Sie nur das Zeug in der öffentlichen Schnittstelle Ihres Besitzer Art verwendet, um # include (die Katze hier wäre). Das macht die Dinge besser für die Menschen Ihrer Bibliothek verwendet und bedeutet, dass Sie nicht über Menschen, abhängig von gewissen inneren Teil Ihrer Bibliothek kümmern müssen - entweder aus Versehen oder weil sie etwas tun wollen können Sie nicht, so dass sie #define private public bevor Sie Ihre Dateien mit.

Wenn es eine einfache Klasse ist, gibt es in der Regel kein Grund, eine Pimpl zu verwenden, aber für Zeiten, in denen die Typen ziemlich groß sind, kann es eine große Hilfe sein (vor allem bei der Vermeidung von langen Bauzeiten)

Nun, ich würde es nicht verwenden. Ich habe eine bessere Alternative:

foo.h:

class Foo {
public:
    virtual ~Foo() { }
    virtual void someMethod() = 0;

    // This "replaces" the constructor
    static Foo *create();
}

foo.cpp:

namespace {
    class FooImpl: virtual public Foo {

    public:
        void someMethod() { 
            //....
        }     
    };
}

Foo *Foo::create() {
    return new FooImpl;
}

Hat dieses Muster einen Namen?

Als auch Python und Java-Programmierer, ich mag diese viel mehr als das Pimpl Idiom.

Wir verwenden PIMPL Idiom um aspektorientierte Programmierung zu emulieren, wo vor, Post und Fehler Aspekte vor und nach der Ausführung einer Member-Funktion aufgerufen werden.

struct Omg{
   void purr(){ cout<< "purr\n"; }
};

struct Lol{
  Omg* omg;
  /*...*/
  void purr(){ try{ pre(); omg-> purr(); post(); }catch(...){ error(); } }
};

Wir haben auch Zeiger auf Basisklasse verwenden unterschiedliche Aspekte zwischen vielen Klassen zu teilen.

Der Nachteil dieses Ansatzes ist, dass die Bibliotheksbenutzer berücksichtigt alle Aspekte zu ergreifen hat, die ausgeführt werden werden, sondern sieht nur seine Klasse. Es erfordert die Dokumentation für irgendwelche Nebenwirkungen durchsuchen.

Platzieren Sie den Aufruf der impl-> Purr in der CPP-Datei bedeutet, dass Sie in Zukunft etwas ganz anderes, ohne tun könnte die Header-Datei zu ändern. Vielleicht entdecken sie im nächsten Jahr eine Hilfsmethode sie stattdessen genannt haben könnte und so können sie den Code ändern, dass direkt aufrufen und verwenden Sie nicht impl-> Purr überhaupt. (Ja, sie könnten das Gleiche erreichen, indem die tatsächlichen impl Aktualisierung :: Purr Methode als gut, aber in diesem Fall sind Sie mit einem zusätzlichen Funktionsaufruf fest, die nichts erreicht, aber die nächste Funktion wiederum Aufruf)

Es bedeutet auch die Header nur Definitionen haben und haben keine Implementierung, die für eine saubere Trennung macht, was der Sinn des Idioms ist.

I umgesetzt gerade meine erste Pimpl Klasse in den letzten paar Tagen. Ich benutzte es, Probleme zu beseitigen, wurde ich in Borland Builder einschließlich winsock2.h mit. Es schien struct Ausrichtung zu vermasseln und da ich Buchse Dinge in der Klasse privaten Daten hatte, diese Probleme zu jeder CPP-Datei ausbreiteten, die den Header enthalten.

Durch die Verwendung von Pimpl, winsock2.h wurde nur in einer CPP-Datei enthalten, wo ich einen Deckel auf das Problem setzen konnte und sich keine Sorgen, dass es zurückkommen würde, mich zu beißen.

Um die ursprüngliche Frage zu beantworten, der Vorteil, den ich in das Weiterleiten der Anrufe an die Pimpl Klasse gefunden wurde, dass die Pimpl Klasse als die gleiche ist, was Ihre ursprüngliche Klasse gewesen wäre, bevor Sie es pimpl'd, plus Ihre Implementierungen sind nicht verteilt auf 2 Klassen auf eine seltsame Art und Weise. Es ist viel klarer die Öffentlichkeit einfach sich auf die Pimpl Klasse zu implementieren.

Wie Herr Nodet sagte, eine Klasse, eine Verantwortung.

Ich weiß nicht, ob dies ein Unterschied erwähnenswert ist, aber ...

Wäre es möglich, die Umsetzung in einem eigenen Namensraum zu haben und hat eine öffentliche Wrapper / Bibliothek Namespace für den Code des Benutzer sieht:

catlib::Cat::Purr(){ cat_->Purr(); }
cat::Cat::Purr(){
   printf("purrrrrr");
}

Auf diese Weise all Bibliotheks-Code Verwendung der Katze Namespace und wie die Notwendigkeit machen kann eine Klasse für den Benutzer zu belichten entsteht ein Wrapper im catlib Namespace erstellt werden kann.

Ich finde es, dass zu sagen, trotz, wie bekannt das Pimpl Idiom ist, sehe ich es sehr oft nicht auftauchen im wirklichen Leben (zum Beispiel in Open-Source-Projekten).

Ich frage mich oft, wenn die „Leistungen“ sind overblown; ja, können Sie einige Ihrer Implementierung machen Details noch versteckt, und ja, können Sie Ihre Implementierung ändern, ohne den Header zu ändern, aber es ist nicht klar, dass diese große Vorteile in der Realität.

Das heißt, es ist nicht klar, dass es keine Notwendigkeit für Ihre Implementierung sein , die gut versteckt, und es ist vielleicht ganz selten, dass die Menschen wirklich nur die Implementierung ändern sich; sobald Sie neue Methoden hinzufügen müssen, sagen, müssen Sie trotzdem den Header ändern.

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