ぬるぽキラー 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()が呼び出され……無限ループします。なので、最初に示したような実装になりました。