私は最近、Java と Ruby から C++ に切り替えましたが、驚いたことに、プライベート メソッドのメソッド シグネチャを変更すると、パブリック インターフェイスを使用するファイルを再コンパイルする必要があります。これは、プライベート部分も .h ファイルにあるためです。
私はすぐに、おそらく Java プログラマーにとって典型的な解決策、つまりインターフェース (= 純粋仮想基底クラス) を思いつきました。例えば:
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);
}
ここでの唯一の問題は、 を使用できずnew
、代わりに を呼び出さなければならないことBananaTree::create()
です。とにかく工場をたくさん使うことを期待しているので、それは本当に問題ではないと思います。
しかし、C++ で有名な賢者たちは、別の解決策pImpl イディオムを思い付きました。それで、正しく理解できれば、私のコードは次のようになります。
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);
}
これは、 のすべてのパブリック メソッド (BananaTree
この場合は ) に対して、デコレータ スタイルの転送メソッドを実装する必要があることを意味しgetBanana
ます。これは、私が要求したくない複雑さとメンテナンスの労力の追加レベルのように思えます。
では、ここで質問です。純粋仮想クラスのアプローチの何が問題なのですか? pImpl アプローチのほうが文書化されているのはなぜですか? 何か見逃しましたか?