More C++ Idioms/コピー禁止ミックスイン(Non-copyable Mixin)
コピー禁止ミックスイン(Non-copyable Mixin)
[編集]意図
[編集]あるクラスのオブジェクトについて、コピーコンストラクタによる構築や代入を防止する。
別名
[編集]動機
[編集]クラスのオブジェクトのコピーを防止することに意味があることが良くある。例えば、ネットワーク接続をカプセル化したクラスを考えてみよう。そのようなクラスに対して意味のあるコピーを定義することは出来ない。そのため、ガイドラインやプログラマに対して課す規律に頼ることなく、明示的にこれらを防止するべきである。可読性を高めるために、一目見ただけで簡単に、その意図が特定可能であるべきである。
解法とサンプルコード
[編集]private なコピーコンストラクタとコピー代入演算子を持つ non-copyable と呼ばれるクラスを定義する。
class NonCopyable
{
protected:
NonCopyable () {}
~NonCopyable () {} /// protected な非仮想デストラクタ
private:
NonCopyable (const NonCopyable &);
NonCopyable & operator = (const NonCopyable &);
};
class CantCopy : private NonCopyable
{};
private な基本クラスである NonCopyable のコピーコンストラクタとコピー代入演算子が派生クラスからアクセス可能ではないため、CantCopy オブジェクトはコピーできない。これを実現する伝統的な方法は、private なコピーコンストラクタとコピー代入演算子を宣言し、なぜそのようにしたかを記述することである。しかし、NonCopyable から派生する方が簡潔明瞭で、追加説明を添える必要もない。 NonCopyable の private メンバは定義する必要がない。NonCopyable はまた、上位からの混入(mixin-from-above)として分類することができる。「上位」から派生クラスに対して、コピー禁止という特徴を混入(mix-in)する再利用可能なモジュールを定義するからである。CRTP に基づく解法を以下に示す。
template <class T>
class NonCopyable
{
protected:
NonCopyable () {}
~NonCopyable () {} /// protected な非仮想デストラクタ
private:
NonCopyable (const NonCopyable &);
T & operator = (const T &);
};
class CantCopy : private NonCopyable <CantCopy>
{};
非 CRTP 版の NonCopyable を使用し多重継承を行った場合、空の基底クラスに対する最適化(Empty Base Optimization)を妨げる場合がある。
struct A : NonCopyable {};
struct B : NonCopyable {};
struct C : A, B {};
この場合、C は同じ基本クラス NonCopyable の 2 つのサブオブジェクトを継承している。規格では、同じ基本クラスの(異なる)サブオブジェクトが異なるアドレスを持つことが要求されている。これにより、上記の場合、空の基底クラスに対する最適化が妨げられ、sizeof(C) は最低でも 2 となる。
以下のようにすれば空の基底クラスに対する最適化を行わせることができる。
struct A : NonCopyable<A> {};
struct B : NonCopyable<B> {};
struct C : A, B {};
この場合、C は同じ型のサブオブジェクトを持たないため、空の基底クラスに対する最適化が可能である。
既知の利用
[編集]関連するイディオム
[編集]Mixin-from-above