PIMPL steht für Private Implementation, das Idiom ist auch bekannt unter den Bezeichnungen Handle-Body Idiom, Cheshire Cat technique und Compilation Firewall technique. Manchmal findet man in der Fachliteratur auch die Übersetzung Pointer To Implementation, was den Kern der Sache wohl am ehesten trifft. Vorgestellt wurde es erstmals vom C++-Experten und Mitglied des ISO C++ Standardisierungs-Komitees Herb Sutter im Guru of the Week (GotW) Nr. 24 am 31. Oktober 1997.
Das Ziel des PIMPL Idioms ist das Minimieren von Abhängigkeiten. Zu viele Abhängigkeiten zwischen Modulen und Komponenten stellen in der Software-Entwicklung nahezu immer ein Problem dar. Es gilt folgende Heuristik: je weniger Abhängigkeiten zwischen einem Modul (Komponente, Klasse, ...) und anderen Modulen existieren, desto einfacher ist die Wiederverwendung dieses Moduls. Zugleich ermöglichen geringe Abhängigkeiten eine entsprechend unabhängige Pflege und (Weiter-)Entwicklung des Moduls. Beim PIMPL geht es vor allem um eine Reduzierung der Übersetzungs- und Testzyklen sowie um ein Abkapseln der Implementierung.
Schauen wir uns dazu doch einmal ein kleines Codebeispiel an:
Wie man sieht, hat Example.hpp konkrete Abhängigkeiten zu std::string, std::vector und zu einem Datentyp OtherType. Wird Example.hpp in einem großen Projekt häufig verwendet und von vielen anderen Dateien inkludiert (beispielsweise wenn Example Bestandteil einer Bibliothek ist), so entsteht das Problem, das alle von Example.hpp abhängigen Module jedes Mal neu kompiliert werden müssen, sobald irgendetwas an der privaten Implementierung von Example geändert wird. Dieser Umstand kann in großen Projekten zu erheblich höheren Kompilier-Zeiten führen.
An dieser Stelle kommt nun der PIMPL ins Spiel: PIMPL ist eine Methode um Implementierungs-Details zu verdecken, in dem private members (Daten und Funktionen) einer Klasse in eine interne, private Struktur bzw. Klasse ausgelagert werden. Diese interne Struktur/Klasse wird deklariert und implementiert in der cpp-Datei! In der Header-Datei existiert nur eine Vorwärtsdeklaration. Hier unser vorheriges Beispiel, diesmal mit PIMPL:
Was als erstes auffällt, ist, das nun alle include-Anweisungen aus der Example.hpp verschwunden sind. Nun ist es möglich ExampleImpl zu modifizieren (beispielsweise kann man den vector<int> durch ein int* ersetzen), ohne dass das gesamte Projekt neu übersetzt werden muss. Selbstverständlich ist es auch möglich, statt des einfachen Zeigers ExampleImpl* einen std::auto_ptr zu verwenden: dieser übernimmt sicher das Speichermanagement für den PIMPL, womit das explizite Zerstören der Instanz von ExampleImpl im Destruktor von Example entfällt.
Das PIMPL idiom hat natürlich auch Nachteile. Jede Speicheranforderung auf dem Heap kostet Zeit, und so kann sich die Konstruktion des Implementierungs-Objekts zur Laufzeit durchaus negativ auswirken, wenn man sehr viele Objekte vom Typ Example benötigt. Darüber hinaus entsteht natürlich auch ein geringer Overhead durch die Aufrufindirektion. Auch wenn diese Faktoren nur in bestimmten Ausnahmefällen eine Rolle spielen, sollte man sich darüber im Klaren sein.




