2006-04-22-01-オブジェクトもリサイクル - project-enigma

2006-04-22-01-オブジェクトもリサイクル

>> Site top >> weblog >> 月別アーカイブ >> 2006年04月のlog >> 2006-04-22-01-オブジェクトもリサイクル

最終更新日付:2014/01/02 00:00:00


オブジェクトもリサイクル

2006 年 04 月 22 日

データ抽象やオブジェクト指向を中心としたデザインでソフトウェアを書いていると、どうしてもヒープ領域を頻繁に使用するようになります。クラスの在り方によっては、ソフトウェアのライフサイクル中にそれこそ膨大な回数 new と delete が繰り返されることにもなります。

ヒープ領域の頻繁な使用がどれくらい問題になるかは環境にも状況にもよります。しかし、一度確保した領域をまた(別のコンテキストででも)使うことができるのであれば、やるに越したことは無いでしょう。これから書くテクニックは別に目新しいものでも難しいものでもありません。昔何かの本で読んだものだと記憶していますが、まぁ最近使うことが多いので書き留めておくにはいい機会です。間違っても陰郎が自分で考え出したなんて主張するものではありませんのでお間違えのないように。

さて、説明のために分かりやすいクラス Foo があるとしましょう。別にどうということもありませんね。しかし、これが膨大な回数 new と delete を繰り返すことになるとします。

class Foo {
private:
    int m_data;
public:
    Foo( );
    Foo( int data );
    ~Foo( );
public:
    // public methods...
};

このクラス Foo のオブジェクトをリサイクルして使うとなると、誰かがそれを管理せねばなりません。お手軽な方法は、Foo クラス自身(Foo オブジェクトではない)にやらせることです。というわけで、クラススコープの CreateInstance() とReturnInstance() を追加し、コンストラクタとデストラクタは private とします。

class Foo {
private:
    int m_data;
private:
    Foo( );
    Foo( int data );
    Foo( const Foo& foo );
    ~Foo( );
public:
    // public methods...
public:
    static Foo* CreateInstance( int data );
    static void ReturnInstance( Foo* wreck );
};

ひとまず、これで Foo オブジェクトを得たければ Foo::CreateInstance() を呼び出す以外に方法はなくなりました。どさくさにまぎれてコピーコンストラクタが追加されてます。これまた private ですからスタック上にオブジェクトを作ることも禁止されます。sizeof(Foo) に等しい領域を用意して reinterpret_cast<Foo*> をかけたり、さらにはFoo のヘッダファイルを #include する前に #define private public とするなどの「荒業」もありますが、それは意図的かつ悪質なタイプシステムの侵犯というものです。

で。使い終わった Foo オブジェクトは Foo::ReturnInstance() を呼ぶことになりますが、Foo はクラススコープでこれをどう管理するのでしょう。そう、まだ必要なステップがあるわけですね。配列を作って格納したりすると拡張が面倒ですから、Foo オブジェクト自体で数珠つなぎをつくりましょう。不要なオブジェクトサイズの膨張を防ぐためにあえて union を使用します。で、数珠つなぎの先頭を掴んでおくためにクラススコープメンバ s_pCemetery( 共同墓地 )を追加します。結果、こんな感じになりました。

class Foo {
private:
    union {
        int m_data;
        Foo* pNext;
    };
private:
    Foo( );
    Foo( int data );
    Foo( const Foo& foo );
    ~Foo( );
public:
    // public methods...
private:
    static Foo* s_pCemetery;
public:
    static Foo* CreateInstance( int data );
    static void ReturnInstance( Foo* wreck );
};

で、まぁキモはクラススコープメンバの使い方なわけですから、それだけ以下に示します。特に難しいことはありません。Foo::CreateInstance() では、s_pCemetery にリサイクル可能なオブジェクトがあればそれを使い、空っぽなら新しいオブジェクトを作ります。Foo::ReturnInstance() では返却された Foo オブジェクトを s_pCemetery による数珠つなぎに追加し、以後のリサイクルに備えるわけです。

Foo* Foo::s_pCemetery = NULL;

Foo* Foo::CreateInstance( int data ) {
    Foo* p = s_pCemetery;
    if( !p )
        p = new Foo( data );
    else {
        s_pCemetery = s_pCemetery->pNext;
        p->m_data = data;
    }
    return p;
}

void Foo::ReturnInstance( Foo* wreck ){
    wreck->pNext = s_pCemetery;
    s_pCemetery = wreck;
}

まぁ大体こんなところなのですが、肝心なことを忘れていますね。new が使用されているにもかかわらず、このコードには delete がまったく見当たらないのです。結局のところ、プログラムの終了時には全ての Foo オブジェクトが墓地に収まっている(嫌な表現だな)はずなのですが、それを開放するのは誰の責任か...ということです。まぁ成り行きからいって Foo クラスにやらせたいのですが、Foo が「自分で」プログラムの終了時を検出することは現状ではできません。では、どうすればよいか。

ここでは面倒なの(と、もうひとつの別の理由があるの)で具体的な方法は書きませんが、プログラムの終了時に破壊されることが保証されている大域変数を利用して Foo クラスの共同墓地をキレイにしてあげればよいわけですね。では今夜はこのへんで。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : 開発

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.