Recentemente sono passato da Java e Ruby a C++ e con mia grande sorpresa devo ricompilare i file che usano l'interfaccia pubblica quando cambio la firma del metodo di un metodo privato, perché anche le parti private sono nel file .h.mantenendo le parti private esterne alle intestazioni C++: pura classe base virtuale vs pimpl
Ho trovato rapidamente una soluzione che è, credo, tipica per un programmatore Java: interfacce (= classi di base virtuali pure). Per esempio:
BananaTree.h:
class Banana;
class BananaTree
{
public:
virtual Banana* getBanana(std::string const& name) = 0;
static BananaTree* create(std::string const& name);
};
BananaTree.cpp:
class BananaTreeImpl : public BananaTree
{
private:
string name;
Banana* findBanana(string const& name)
{
return //obtain banana, somehow;
}
public:
BananaTreeImpl(string name)
: name(name)
{}
virtual Banana* getBanana(string const& name)
{
return findBanana(name);
}
};
BananaTree* BananaTree::create(string const& name)
{
return new BananaTreeImpl(name);
}
L'unica seccatura qui, è che io non posso usare new
, e devo invece chiamare BananaTree::create()
. Non penso che sia davvero un problema, soprattutto perché mi aspetto comunque di usare molto le fabbriche.
Ora, i saggi della fama C++, tuttavia, hanno trovato un'altra soluzione, la pImpl idiom. Con questo, se ho ben capito, il mio codice sarà simile:
BananaTree.h:
class BananaTree
{
public:
Banana* addStep(std::string const& name);
private:
struct Impl;
shared_ptr<Impl> pimpl_;
};
BananaTree.cpp:
struct BananaTree::Impl
{
string name;
Banana* findBanana(string const& name)
{
return //obtain banana, somehow;
}
Banana* getBanana(string const& name)
{
return findBanana(name);
}
Impl(string const& name) : name(name) {}
}
BananaTree::BananaTree(string const& name)
: pimpl_(shared_ptr<Impl>(new Impl(name)))
{}
Banana* BananaTree::getBanana(string const& name)
{
return pimpl_->getBanana(name);
}
Ciò significherebbe che devo implementare un decoratore -stante metodo di inoltro per ogni metodo pubblico di BananaTree
, in questo caso getBanana
. Questo mi sembra un livello aggiunto di complessità e sforzo di manutenzione che preferisco non richiedere.
Quindi, ora per la domanda: cosa c'è di sbagliato nell'approccio di classe virtuale pura? Perché l'approccio pImpl è molto meglio documentato? Mi sono perso qualcosa?