ぷろみん

プログラミング的な内容を扱ってます

RAIIから考える良いクラス設計

モチベーション

良いクラス設計を考える。

RAII

RAII(Resource Acquisition Is Initialization) リソースの確保を初期化時に行う手法。

拡大解釈してInit関数作るから大丈夫って考え方もあるみたいですが、私は否定的です。 可能ならばコンストラクタで全ての初期化を行うべきです。

Init関数の駄目な所

Init関数が存在するということはInit関数が呼ばれるまで、そのクラスは不安定な状態となってしまいます。

Foo foo;
foo.Execute(); // エラー?何もしない?ユーザーには分からない
foo.Init();
foo.Execute();

これは使いにくいし、他のプログラマーはクラスの所有する関数のチェックから始めなければいけません。

また、Init関数があるとRAIIの思想を邪魔してしまいます。

Foo1() :
    foo2_( 0 ),
    foo3_( foo2_ ) // foo2_はInit()しないと駄目なクラスなのでこれは駄目
{
}

わざわざ以下のようにする必要があります

std::unique_ptr<Foo3> foo3_;
Foo1() :
    foo2_( 0 )
{
    foo2_.Init();
    foo3_.reset( new Foo3( foo2_ ) );
}

何故コンストラクタで初期化を終わらせると良いのか

コンストラクタで初期化が終わっていると、そのクラスは生成された時点で安定という事になります。アクセスするタイミングや必要な物について考える必要がなくなります。これはつまり、メンバに持つ事ができるということです。
メンバに持つ事ができるという事は廃棄についても考えなくて良いし、動的メモリ確保も必要なくなります。これが正しいオブジェクトのあり方のはずです。

デストラクタは極力使わない

C++ではデストラクタでの解放を必要としないクラスが好まれています。これは、最後に解放が必要なクラスが暗黙的にリソースをリークするからです。何もしないとリークするよりは、何もしないでも解放されている方が楽ですし、それを期待します。その期待を裏切らないクラス作りを心がけましょう。