pImpl-Idiom und öffentliche Vererbung
-
Hallo Leute,
ich würde gerne "Pointer to implementation" einsetzen. Nun bin ich mir nicht ganz sicher, was ich da mit der Elternklasse anstellen soll.
Derzeit ist die Situation folgende:
class Parent { public: virtual void foo(); protected: Bar* b; }; class Derived: public Parent { public: virtual void foo() {doSomethingWithBar(b);} };Für derived möchte ich jetzt pimpl benutzen, also
class DerivedImpl() : public Parent{ public: void foo(); {doSomethinghWithBar(b);} }; class Derived : public Parent { public: virtual void foo() {pimpl_->foo();} private: DerivedImpl* pimpl_; };Damit ich Derived nachwievor einsetzen kann, muss Derived von Parent erben. Damit aber DerivedImpl b benutzen kann, muss DerivedImpl von Parent erben.
Ist das sinnvoll? Dann habe ich ja zwei "Kopien" von Parent, einmal in Derived und einmal in DerivedImpl, wobei DerivedImpl ja von Derived besessen wird.
Wie geht man damit üblicherweise um?
Gruß,
Philipp
-
PhilippM schrieb:
Damit ich Derived nachwievor einsetzen kann, muss Derived von Parent erben.
Kein Problem...
PhilippM schrieb:
Damit aber DerivedImpl b benutzen kann, muss DerivedImpl von Parent erben.
Warum? Warum sollten die Implementationsdetails von der Basisklasse erben müssen?
Ich lasse im folgenden, zur Vereinfachung, die in diesem Fall nötigen Destruktoren, Konstruktoren, Kopierkonstruktoren & Zuweisungsoperatoren usw. weg.
// Parent.h //... class Parent { public: virtual void foo(); protected: Bar* b; };// Derived.h //... class Derived : public Parent { public: virtual void foo(); private: struct DerivedImpl; DerivedImpl * impl_; };// Derived.cpp //... struct DerivedImpl::Derived { //... (Ich setze hier übrigens selten Methoden ein, sondern halte // meist hier nur die Daten) }; //... void Derived::foo() { // Hier irgendwas mit impl anstellen }; //...
-
asc schrieb:
Warum? Warum sollten die Implementationsdetails von der Basisklasse erben müssen?
Weil ich b in der Funktion foo() benutzen muss. Und b ist nunmal im Parent-Teil von Derived gespeichert.
// Derived.cpp //... struct DerivedImpl::Derived { //... (Ich setze hier übrigens selten Methoden ein, sondern halte // meist hier nur die Daten) };Du meinst wohl Derived::DerivedImpl, oder?
Philipp
-
Wenn du "Bar" in DerivedImpl benötigst, übergib es doch im Konstruktor von DerivedImpl.
-
Im konkreten Fall brauch ich allerdings mehrere Member und Funktionen von Parent. Bei einem einzigen Pointer ließe ich ja noch mit mir reden, aber so ...
-
Dann übergib nicht Bar* sondern Derived*, und lass DerivedImpl friend von Derived sein... ABER schön ist das nicht. Design überdenken?
-
PhilippM schrieb:
asc schrieb:
Warum? Warum sollten die Implementationsdetails von der Basisklasse erben müssen?
Weil ich b in der Funktion foo() benutzen muss. Und b ist nunmal im Parent-Teil von Derived gespeichert.
"b" hat im Member-Bereich von Parent gar nix zu suchen! Wenn Pimpl dann bitte richtig! Wenn Derived von Parent erbt, Parent aber nicht gepimplt wird, macht Pimpl bei Derived auch keinen Sinn.
Wenn deine Impl den Member braucht, gib ihn ihm.
Ganz knapp skizziert:class Parent { struct Impl; Impl* impl; protected: Bar* bar() { return impl->b; } }; struct Parent::Impl { Bar* b; }; class Derived : public Parent { struct Impl; Impl* impl; public: void callImplFoo() { impl->foo(bar()); } }; struct Derived::Impl { void foo(Bar* b) { doSomethingWith(b); } };Wenn dich der Parameter in Derived::Impl stört: Du Pimplst, Derived::Impl ist die Implementierung dessen, was sonst im private und/oder protected-Teil steht - das geht eh niemanden was an, Aufurfe kommen nur von Derived. Wenn doch - Pech, musst du den Aufruf auf Derifed::Impl::foo über einen Derived-Methodenaufruf delegieren - wie eben oben gezeigt. Oder protected getter anbieten, oder friend, usw.
Was in den IMPL-Teil rein kommt, kann man sich streiten. Ich leg neben den Membern gern auch mal nützliche Funktionen rein, die sonst niemanden interessieren. Hält die Klassendefinition sauber. Aufrufen tut es aber nur die Klasse die gepimpelt wird.