2009年02月06日

変数の初期化ミス

昨年末にリリースしたドールズイングラムですが、ユーザーからの報告で時々上下スクロールしなくなるというバグが発生していました。原因を調査しようにもウチのデバック環境ではまったく再現しなかったのでほとほと困り果てていたのですが、このたびやっと原因が分かって1.02の修正パッチを出すことができました。

そこで、反省と今後の対処も踏まえて原因を振り返ってみます。

問題の箇所はスクロールの状態を示すメンバ変数でした。これを設計した段階ではコンストラクタ時に必ずA,B,Cのいずれかの状態に設定され問題なく動作していたのですが、後の機能拡張時に初期化処理をコンストラクタではなく、専用の初期化関数のなかでやるように設計を変更したのです。こうすることによってスタート時や再スタート時、コンティニュー時等どの開始時にも同じ箇所で初期化できるのでコード的には良いリファクタリングだったのですが……。
その代りに内部で条件分岐しながら初期化処理を行わなければならないという複雑さがプラスされてしまったのです。

ちなみにC言語は未初期化の変数の中身は何が入っているか分からないというのが仕様なので必ず自分で初期化するのが常識です。しかしそのための初期化関数がいろんな箇所から呼ばれる場合、例えば偶然にもたまたま「初期済み」という状態がその変数に入っていたとしたら……。

はい、今回のバグの原因はその偶然でした。

それでもちゃんとリファクタリング時にチェックして正常に処理することは確認していたのですが、そのテストがデバックモードだったので本番のリリースモードでのコンパイルとは少し条件が違ってしまったのです。
再現性が低いのとデバッガー使用時にはまったく再現しなかったので原因を見つけるのが困難でした。

このバグの対処方法は、コンストラクタ初期化子で「未初期化」という状態を入れておくことで解決しました。今後この手のバグが起きないようにするには、すべてのメンバ変数を必ずコンストラクタ初期化子で一回初期化しておくという方法で解決できます。
しかし、これだと二回初期化することになってしまい、コードも2箇所に書かなければいけないというのはエレガントではない気もします。
でも、次期C++仕様(C++0x)ではメンバ変数の初期化を
int state = 0;
のように宣言時に書けるのでこれならば、スマートのまま今回のようなバグを未然に防ぐことができそうです。

つか早くC++0x実装のコンパイルでコードを書きたい……。
posted by 鶴 at 05:29| Comment(0) | TrackBack(0) | プログラム系