Pimpl idiom mit Vererbung
Möchte ich pimpl-idiom mit Vererbung.
Hier ist die base public-Klasse und deren Implementierung-Klasse:
class A
{
public:
A(){pAImpl = new AImpl;};
void foo(){pAImpl->foo();};
private:
AImpl* pAImpl;
};
class AImpl
{
public:
void foo(){/*do something*/};
};
Und ich möchte in der Lage sein, erstellen Sie die abgeleitete öffentliche Klasse mit der Implementierung der Klasse:
class B : public A
{
public:
void bar(){pAImpl->bar();}; //Can't do! pAimpl is A's private.
};
class BImpl : public AImpl
{
public:
void bar(){/*do something else*/};
};
Aber ich kann nicht mit pAimpl in B, weil es ist Eine private.
So, ich sehe einige Möglichkeiten, es zu lösen:
- Erstellen BImpl* pBImpl Mitglied in B, und übergeben es an Eine mit zusätzlichen Konstruktor, Eine(AImpl*).
- Ändern pAImpl geschützt werden (oder fügen Sie eine Get-Funktion), und verwenden Sie es in B.
- B sollte nicht die Erben von A. Erstellen BImpl* pBImpl Mitglied in B, und erstellen foo() und bar() in B, die pBImpl.
- Andere Weise?
Was soll ich wählen?
Du musst angemeldet sein, um einen Kommentar abzugeben.
A.h
,B.h
,AImpl.h
undBImpl.h
lösen das Problem, das Sie erwähnen?Ich denke, der beste Weg von einer rein Objekt-orientierten-theoretischen Perspektive ist nicht zu machen, BImpl Erben von AImpl (ist das, was Sie meinten in option 3?). Allerdings haben BImpl stammen aus AImpl (und übergeben das gewünschte impl um einen Konstruktor von A) ist OK, vorausgesetzt, dass die pimpl-Membervariable ist
const
. Es spielt eigentlich keine Rolle, ob Sie ein get-Funktionen oder den direkten Zugriff auf die variable von abgeleiteten Klassen verwendet werden, es sei denn, Sie erzwingen möchten, const-correctness, auf die abgeleitete Klassen. Lassen abgeleiteten Klassen ändern pimpl, ist keine gute Idee - Sie könnte Wrack all die Initialisierung und noch ist, läßt sich die Basisklasse ändern, ist es eine gute Idee. Betrachten Sie diese Erweiterung zu deinem Beispiel:Obwohl Sie möglicherweise nicht über eine swap () - Funktion, können Sie leicht vorstellen, dass ähnliche Probleme auftreten, vor allem, wenn belegbar ist, ob durch Unfall oder Absicht. Es ist ein etwas subtiler Verletzung des Liskov Substituierbarkeit Prinzip. Die Lösungen sind entweder:
Nicht ändern, die pimpl-Mitglieder nach dem Bau. Erklären Sie
AImpl * const pimpl
. Dann die abgeleitete Konstruktoren übergeben werden können eine angemessene Art und dem rest der abgeleiteten Klasse können niedergeschlagen zuversichtlich. Aber dann kann man nicht z.B. nicht-werfen swaps, Zuweisungen oder copy-on-write, weil diese Techniken erfordern, dass Sie ändern können, die pimpl-Mitglied. Aber aber, du bist wohl wirklich nicht die Absicht, diese Dinge zu tun, wenn Sie haben eine Vererbungshierarchie.Haben nicht (und dumm) und AImpl BImpl Klassen für A und B ist die private-Variablen,. Wenn B will etwas tun, um Einem, dann mit Einem der public oder protected Schnittstelle. Diese behält auch der häufigste Grund für die Verwendung pimpl: in der Lage zu verbergen, die definition von AImpl Weg in eine cpp-Datei, die von abgeleiteten Klassen nicht verwenden können, so dass die Hälfte des Programms nicht erneut kompilieren müssen, wenn Eine Umsetzung der änderungen.
Als stefan.ciobaca sagte, wenn Sie wirklich wollte, Eine zu sein, erweiterbar, würden Sie wollen
pAImpl
geschützt werden.Jedoch Ihre definition in
B
vonvoid bar(){pAImpl->bar();};
scheint seltsam, wiebar
ist eine Methode, die aufBImpl
und nichtAImpl
.Gibt es mindestens drei einfache alternativen, die zu vermeiden wäre, die Frage:
BImpl
erstrecktAImpl
(Erben der bestehenden Umsetzung vonfoo
eher als die Definition eine andere)BImpl
definiertbar
, undB
verwendet seinen privatenBImpl* pBImpl
Zugriff auf beide.B
hält privaten Zeiger auf die einzelnenAImpl
undBImpl
und leitet jeden derfoo
undbar
zu den entsprechenden Umsetzer.Ich tun würde, (1), da die Soldaten sind oder kein Geschäft für B.
Eigentlich würde ich nicht übergeben es an Eine wie Sie Sie vorschlagen, weil Eine macht seine eigenen in A::A().
Aufruf
pApimpl->whatever()
von Bis auch nicht angemessen (private bedeutet privat).Der richtige Weg ist, das zu tun (2).
Im Allgemeinen, Sie sollten wahrscheinlich betrachten, um alles, was Sie member-Variablen standardmäßig geschützt, um stattdessen private.
Der Grund, die meisten Programmierer, wählen Sie "privat" ist, dass Sie denken nicht über andere, die möchten, leiten aus Ihrer Klasse und die meisten Einführungs-C++ - Handbücher lehren dieser Art, in dem Sinne, dass alle die Beispiele verwenden private.
BEARBEITEN
Code-Duplizierung und Speicherverwaltung zu unerwünschten Nebeneffekten bei der Verwendung der Zuhälter design pattern und können nicht vermieden werden meines Wissens.
Wenn Sie brauchen, um Bimpl Erben Aimpl und Sie wollen zu setzen eine konsistente Schnittstelle, um Sie durch A und B, B müssten auch die Erben, A.
Eine Sache, die Sie tun können, um Dinge zu vereinfachen, in diesem Szenario ist B erbt von A und ändern Sie nur den contructor, so dass B::B(...) {} erstellt ein Bimpl, und fügen Sie löst für alle Methoden der Bimpl, die nicht in Aimpl.