C++ の小さな技術を紹介するシリーズ【小技C++ 全９回】＃７＜続・オブジェクトの隠ぺい＞

#pimpl の性質
**pimpl** のテクニックを利用した【オブジェクトの隠ぺい】は、ポインタの特徴を利用したものだ。

多くの場合、以下のような形で実装されている。
##内部実装クラス（隠ぺいしたいクラス）
```cpp
class CatImpl
{
public:
    void     Naku() { printf( "鳴いたのは zu 回目です。\n", naku_count_++; ); }
public:
    virtual ~CatImpl();
private:
    // 隠ぺいしたい変数
    size_t   naku_count_ = 0;
};
```
##公開クラス
###ヘッダ
```cpp
class Cat
{
    // 隠ぺいしたいクラスをポインタで参照する
    using Impl = std::unique_ptr<CatImpl>;
public:
    // 公開インターフェイス
    void       Naku();
public:
    // ctor
               Cat();
private:
    const Impl impl_;
};
```
###実装
```cpp
void Cat::Naku()
{
    return impl_->Naku();
}

Cat::Cat()
    : impl_( std::make_unique<CatImpl>() )
{
}
```
この方法をよく見てみると、**Impl** クラスにヒープ領域を割り当てることが前提とされたコードとなっていることがわかる。公開クラスのインスタンスを生成する頻度やサイズによっては、この挙動が望ましくない場合もある。（ヒープ領域の消費に伴う問題として、メモリフラグメンテーションは誰もが知るところだろう。）
#Implクラスに割り当てる領域について考える
単刀直入にいうと、公開クラス側に**Impl** クラスを配置するメモリ領域を持たせてしまおう、というアイデアを試してみる。
##ImplContainer
公開クラスが**Impl** クラスを保持すると同時に、その配置領域を提供できるようにするためのヘルパクラスを定義する。

```cpp
class Impl
{
public:
	virtual ~Impl() {};
};
```

```cpp
template<typename T, size_t N = 256>
class ImplContainer
{
	struct lazy_deleter
	{
		void operator()( T* p ) const { static_cast<Impl*>(p)->~Impl(); }
	};

	using InstanceHolder = std::unique_ptr<T, lazy_deleter>;
	using InstanceBuffer = std::array<uint8_t, N>;

public:

	template<typename... Args>
	static auto GetCreator( Args&&... args )
	{
		return [&]( InstanceBuffer& buffer )
		{
			return InstanceHolder( new ( buffer.data() ) T( std::forward<Args>(args)... ) );
		};
	}

	ImplContainer( const std::function<InstanceHolder(InstanceBuffer&)>& f )
		: holder_( std::move(f( buffer_ )) )
	{
	}

	const InstanceHolder& operator->() const
	{
		return holder_;
	}

private:

	InstanceHolder holder_;
	InstanceBuffer buffer_;
};

```
###解説
####InstanceHolder
```cpp
	using InstanceHolder = std::unique_ptr<T, lazy_deleter>;
```

**Impl** クラスを保持するためのポインタとして機能する。**型T**は、**Impl**クラスを継承した任意のクラスをとる。std::unique_ptr と同等の仕事をするが、deleter を書き換えてある。これは、**delete**の呼び出しを抑制し、デストラクタの呼び出しだけを行いたいからである。
####InstanceBuffer
```cpp
	using InstanceBuffer = std::array<uint8_t, N>;
```

**Impl** クラスを実際に配置するメモリ領域となる。公開クラスを定義する時点では **Impl** クラスの正確なサイズを知ることはできない。そのため、固定長サイズの指定で事前に決めておく必要がある。
####コンストラクタ

```cpp
	ImplContainer( const std::function<InstanceHolder(InstanceBuffer&)>& f )
		: holder_( std::move( f( buffer_ ) ) )
	{
	}
```

**Impl** クラスの正確なサイズをとることができない理由と同じく、**ImplContainer** のコンストラクタでは、**InstanceHolder** に直接インスタンスを構築することができない。そのため、**Impl** クラスのインスタンスを生成するための関数を受け取るようにしておく。そして、その関数に**InstanceBuffer** を渡し、**placement new** によりインスタンスを構築させる。

#適用
**ImplContainer** を使って、**Cat** および **CatImpl** クラスを定義しなおしてみる。
##内部実装クラス（隠ぺいしたいクラス）
```cpp
class CatImpl : public Impl // <=== Impl を継承させる
{
public:

	void     Naku();
	virtual ~CatImpl();

private:

	size_t   naku_count_ = 0;
};
```
##公開クラス（ヘッダ）
```cpp
class Cat
{
public:
	void Naku();
	     Cat ();

private:
	const ImplContainer<CatImpl> impl_; // <=== ImplContainer
};
```
##公開クラス（実装）
```cpp
Cat::Cat()
        // ↓=== 生成関数を渡す（関数は ImplContainer が提供してくれる）
	: impl_( ImplContainer<CatImpl>::GetCreator() )
{
}
```

#実例
```cpp
int main()
{
    {
        Cat cat;     // <=== CatImpl はスタック上に構築される
        cat.Naku();

        // <=== スコープを抜けるときに、Cat のデストラクタが呼ばれる
    } 

    return 0;
}
```
#２種類の最適化
**Impl** を使った記述法は、インターフェイスを洗練させるためにとても有効な手段だった。これは**コーディング行為そのもの**における最適化となる。次はコーディングされたプログラムが実際に動作したらどういった影響が出るのかについて深く考察してみる必要がある。つまり、 **パフォーマンスの最適化**だ。それはプログラム動作の安定性を保証するためには欠かせない技術といえる。

よいプログラムとは、これらのどちらも欠けていないと思えるものだろう。日々精進…。