ぬるぽキラー aka Lazy Initialization

Lazy Initializationは主にJavaの世界で使われる用語だと思うのですが、最近C++でこれが必要になってきました。この方法を最初に知ったのは、EclipseのVisual Editorを使ってたときです。本来、Lazy Initializationは「必要になるまでオブジェクトを生成しない」という意味を持っています(参考:Essential Java Styleのエッセンス(^^;Lazy initialization - Wikipedia)。

Lazy Initializationによって実現できるのは、ないはずがないものの実装です。シングルトンみたいなものですが、インスタンス内で唯一なものです。具体的に言えば、ダイアログ上のコントロールとか。いくらないはずがないものでも、どこかの時点で初期化する必要があります。Lazy Initializaionによって、明示的な初期化が不要になります。実装例は以下の通りです。

Button* GetSomeButton() {
  if (mSomeButton == NULL) {
    try {
      mSomeButton = new Button();
      mSomeButton->SetName("ぼたんだよ");
      mSomeButton->SetPosition(20, 20);
    } catch (exception&) {
      delete mSomeButton; mSomeButton = NULL;
      throw; // 例外を垂れ流す
    }
  }
  return mSomeButton;
}

例外を受け取ったときにmSomeButtonを初期化するのは、GetSomeButtonをアトミックな処理にするための仕掛けです。これが無いと問題が起こるケースがあります。SetName()などが例外を投げた場合、mSomeButtonが中途半端な状態になります。わざわざそんなことしなくても、

Button* GetSomeButton() {
  if (mSomeButton == NULL) {
    auto_ptr<Button> tmp(new Button());

    tmp->SetName("ぼたんだよ");
    tmp->SetPosition(20, 20);

    mSomeButton = tmp.release();
  }
  return mSomeButton;
}

でいい気がしたのですが、Windowsマジックによって却下されました。上のは一例なので実際のコードとは違うのですが、要はSetName()などがGetSomeButton()を呼び出してしまうことがあるのです。もちろんSetName()が呼ばれた時点ではmSomeButtonはNULLなので、再びボタン生成プログラムを実行し、SetName()が呼び出され……無限ループします。なので、最初に示したような実装になりました。