趣味で作ってるロボット用ソフトウェア
 All Classes Files Functions Enumerations Enumerator Friends Pages
pimpl イディオム

pImpl イディオムとは C++ のクラス設計において private 変数や private メソッドをヘッダファイルに配置しないための手法です。
このプロジェクトでは、多くの C++ のクラスで pImpl イディオムを使っています。

pImpl を使う場合と使わない場合の比較

例として、長方形の辺の長さをコンストラクタで受け取り、面積を返すクラスを考えます。 pImpl イディオムを使わない場合、例えば以下のような実装になります。

ヘッダ

class Rectangleon
{
public:
Rectangleon(double x, double y);
double area(void) const;
private:
Rectangleon(void);
double x_;
double y_;
};

ソース

Rectangleon::Rectangleon(double x, double y) : x_(x), y_(y)
{
}
double Rectangleon::area(void) const
{
return x_ * y_;
}

また pImpl を使った場合は、以下のような実装になります。

ヘッダ

#include <memory>
class Rectangleon
{
public:
Rectangleon(double x, double y);
~Rectangleon(void);
double area(void) const;
private:
Rectangleon(void);
struct pImpl;
std::auto_ptr<pImpl> pimpl;
};

ソース

struct Rectangleon::pImpl
{
double x_;
double y_;
pImpl(double x, double y) : x_(x), y_(y)
{
}
double area(void) const
{
return x_ * y_;
}
};
Rectangleon::Rectangleon(double x, double y) : pimpl(new pImpl(x, y))
{
}
Rectangleon::~Rectangleon(void)
{
}
double Rectangleon::area(void) const
{
return pimpl->area();
}

pImpl イディオムを使うと、ヘッダファイルから private 変数の x_, y_ という変数がなくなるかわりに pimpl という private 変数が追加されています。また、ソースコードからは area() 関数が pImpl 構造体に移動しています。

この変更により実現できたことは「ヘッダファイルから実装の詳細を取り除く」ということです。このことにより、

という利点が得られます。

大抵の場合、ヘッダファイルが変更されると、そのヘッダファイルを include しているソースコードのコンパイルも必要になります。つまり Rectangleon.h というファイルを3つのソースコードが include している場合は、それら 3つのソースコードの再コンパイルが必要になります。

dot_inline_dotgraph_3.png

pImpl イディオムを用いて、コードの変更が Rectangleon.h から Rectangleon.cpp になれば、再コンパイルが必要になるのは Rectangleon.cpp 1つのみにできます。

また、最後に pImpl イディオムを導入するデメリットを説明します。
pImpl イディオムを導入すると、クラスのコンストラクタ時に pImpl オブジェクトを new するコストと pimpl の変数やメソッドにアクセスするときに pimpl オブジェクトを介してアクセスするだけのコストが生じます。また、コードは多少繁雑になります。

参考文献