More C++ Idioms/具象データ型(Concrete Data Type)

具象データ型(Concrete Data Type)
編集

意図 編集

フリーストア(ヒープ)を用いた動的な割り当てを許可あるいは禁止する事でオブジェクトのスコープと生存期間を制御する。

別名 編集

動機 編集

C++ にはオブジェクトの生存期間の制御とプログラムレベルでの識別子(変数)への束縛に対して 2 つの方法がある。1 つ目は、スコープ変数とオブジェクトであり、スコープが終了した後速やかに破棄される(例えば 関数スコープの整数変数等)。2 つ目は、スコープ変数(しばしばポインタ)とフリーストアに動的に割り当てられたオブジェクトである。この場合、変数のスコープが終了した際に変数自体は存在しなくなるがオブジェクトの生存期間は継続する(例えば シングルトンやウィンドウオブジェクト等)。 具象データ型(Concrete Data Type)イディオムを用いる事で、生存期間に対する上記 2 つの選択のいずれかを強制する事が出来る。

解法とサンプルコード 編集

このイディオムは、目的を達成する為に単純にクラスレベルのアクセス指定子(private, protected)を使う。以下のコードは MouseEventHandler クラスに動的な割り当てを強制する方法を示す。

class EventHandler 
{
  public:
    virtual ~EventHandler () {}
};
class MouseEventHandler : public EventHandler // 継承に注目。
{
  protected:
    ~MouseEventHandler () {} // protected な仮想デストラクタ。
  public:
    MouseEventHandler () {} // public なコンストラクタ。
};
int main (void)
{
  MouseEventHandler m; // デストラクタが protected なのでスコープ変数は許可されない。
  EventHandler *e = new MouseEventHandler (); // 動的な割り当ては許可される。
  delete e;  // 多態的な delete はメモリリークを起こさない。
}

動的な割り当てを強制する別の方法として、コンストラクタへの直接のアクセスをさせず代わりに動的に割り当てられたオブジェクトを返す静的関数 instance() を提供する方法がある。これはシングルトンデザインパターンとよく似ている。さらに言えば、メモリを解放するのに多態的な delete は必ずしも必要ではない。メンバ関数 destroy() によって仮想関数テーブルポインタに必要な領域を節約した上で目的を達成する事が出来る。

class MouseEventHandler // 継承がない点に注目。
{
  protected:
    MouseEventHandler () {} // protected なコンストラクタ。
    ~MouseEventHandler () {} // protected で非仮想なデストラクタ。
  public:
    static MouseEventHandler * instance () { return new MouseEventHandler(); }
    void destroy () { delete this; }  // メモリを解放する。
};

このイディオムの対極に当たるのがスコープ変数(自動変数)の強制である。これは private な new 演算子を用いる事で実現できる。

class ScopedLock
{
  private:
    static void * operator new (unsigned int size); // 動的な割り当てを許可しない。
    static void * operator new (unsigned int size, void * mem);  // 配置形式(placement) new も同様に許可しない。
};
int main (void)
{
   ScopedLock s; // 可能。
   ScopedLock * sl = new ScopedLock (); // 標準の new 及び nothrow な new は許可されない。
   void * buf = ::operator new (sizeof (ScopedLock));
   ScopedLock * s2 = new(buf) ScopedLock;  // 配置形式(placement) new も許可されない。
}

ScopedLock オブジェクトは標準的な new 演算子、nothrow な new、配置形式(placement) new のいずれによっても動的に割り当てる事が出来ない。

既知の利用 編集

関連するイディオム 編集

References 編集