PImpl

PImpl イディオムは、ヘッダー内での include 数を減らしたい場合や実装を隠蔽する時に使用されるイディオムです。
id:melpon さんの C++ でのビルド時間を短縮するいくつかの方法 にチラッと出てきたアレです。
便乗ヒャッホイ
そういえばあんまり使ったことがないなー、と思いちょっとまとめ。


[*.h]

#ifndef _FILE_H_
#define _FILE_H_

class file{
public:
    file();
    file(const file& rhs);
    file& operator =(const file&);
    ~file();

    void read(const char* file_name);
    void show();
private:
    class impl;
    impl*    pimpl;
};

#endif    // #ifndef _FILE_H_


[*.cpp]

#include "file.h"

#include <vector>
#include <string>

// この中にガシガシと実装していく
class file::impl{
public:
void read(const char* file_name){
    // 〜ファイルの読み込み〜
}
void show(){
    // 〜ファイルの表示〜
}
private:
    std::vector<std::string> line_data;
};


file::file()
    : pimpl(new file::impl()){}

file::file(const file& rhs)
    : pimpl(new file::impl(*rhs.pimpl)){}

file::~file(){
    delete pimpl;
}

file&
file::operator =(const file& rhs){
    impl* tmp = pimpl;
    pimpl = new file::impl(*rhs.pimpl);
    delete tmp;
    return *this;
}

// impl の呼び出しのみ
void
file::read(const char* file_name){
    pimpl->read(file_name);
}
void
file::show(){
    pimpl->show();
}


まぁフタを開けてみれば、そんなに難しい事をやっているわけじゃないですね。
ヘッダー側でインナークラスを宣言して、その中に実装をガシガシと書いて行きます。
別のクラスに実装を記述するという意味では、インターフェースの様な感じかな?
あっちも隠蔽性は高いし。
こうすれば、変数の宣言をヘッダー内でする必要がなくなるので、ヘッダー内での include 数を減らすことが出来ます。


しかしながら引数や戻り値型に使う場合は、当然 include が必要となります。

#include <string>

class file{
public:
    // std::string を使いたい
    const std::string& file_name();
};

まぁこれもヘッダー内でクラスの宣言だけ書いておけば解決するんですが…。
string や vector なんかのちょっと複雑な template クラスだと手間なので、今回はやめておきます。


と、まぁざっとまとめればこんな感じですね。
本来であれば、代入演算子やコピーコンストラクタなんかも実装しないとダメなんですが、今回は割愛しました。
※追記しました。
詳細は、↓のページを参照してください。
ファイルの依存関係を減らす事もいいんですが、実装を隠蔽するのが結構面白いです。
今までライブラリなんかを作る場合は、インターフェースを使用していたのですが、こっちの方が楽そうですね。


[追記]
コメントでツッコまれたので、代入演算子とコピーコンストラクタを追記。
やっぱり手抜きはダメですね。
ご指摘ありがとうございます。
これで合ってる…よね?


[参照]
http://ameblo.jp/woooh-p/entry-10053200172.html
http://d.hatena.ne.jp/melpon/20101022/1287705842