未初期化変数

Stroustrup先生の本が届いたので読み始める。C++構文の選択理由について色々書いてあっておもしろい。

C++の設計と進化

C++の設計と進化

一つ意外だった(でも嬉しかった)のは、未初期化変数を減らす工夫について書いてあったこと。未初期化変数というのは、初期化されていない使用可能な変数のことで、例えば、以下のようなもの。

int i;  // 変数の宣言
...  // 何らかのコードがある
i = 0;  // ここで初めて値がセットされる

変数iが、プログラムの中で参照可能になってから(宣言時)、正当に使用可能になる(初期化時)までに時間的な差がある。すなわち、変数iは、「未初期化」「初期化済み」の二つの状態をもつことになる。C++の構文は(Cと違い)、変数宣言の位置をスコープの先頭に限らないので、上のプログラムは宣言と初期化を同時に行うように書き換えることができる。しかし、C++の仕組みも完全ではなく、例えば、以下のようなプログラムでは、宣言と初期化のタイミングを同一にはできない。

int i;  // 変数の宣言
cin >> i;  // ユーザーからの入力値でiを初期化する

Stroustrupは以下のように書いている。

私が言語を完全にゼロから設計していたら、たぶんAlgol68の流儀を一歩推し進めて、あらゆる文と宣言を、値を持つ式にしただろう。そして未初期化変数というものを完全に禁じ、また一つの宣言の中で複数の名前を宣言することも許さないだろう。

そういうのも見てみたいな。ちなみに、僕がそういうのを作るとしたら、

  • 変数の状態が可能な限り一つに定まるように、プログラマを強制する。
  • 変数の状態が二つになる場合は、「デフォルト値」というものをプログラマが導入できるようにする。
  • 変数の状態が二つ以上、かつ変数の「デフォルト値」がそぐわない場合は、「変数が状態を持っている」ことの明示的な宣言を必須とする。これによって、複雑さを管理・計測しやすくなる。

世の中のプログラマのほとんどは、未初期化変数についてあまり考えない。さらに言うと、「変数に状態を持たせること」が、プログラムの複雑度をどう押し上げるのかについて考えたことがある人はいないようだ。「コードコンプリート」でさえも、その点はいまいちだった。

以前、Java言語を使っているときに、僕は、仲間に「未初期化変数をやめよう」と提案したことがある。僕の意図は、変数の生存期間を意識したプログラミングを行うことにあった。

驚いたことに、その提案はあっさりと同意された。しかし、その後が笑い話しだった。彼のコードは、これまで以下のようなものだったが、

Object a;
Object b;
...

これが、以下のように変わった。

Object a = null;
Object b = null;
...

これを見て、僕は、かなりショボーンとしたのを覚えている。

ポインタをnullにセットするのは、Cでは意味がある。

  • 何もセットしないと、メモリのどこを指すかが定義されず、ポインタ使用時に挙動が未定義になる。それならば、明示的に実行時エラーになるほうがまし。
  • メモリ解放後にnullにセットする。一度解放したメモリをもう一度解放するのを避けるため。

これらも、エラーを避けるための工夫ではあるが、未初期化変数とは関係ない。というか、これらはJavaではまったく不要だ(Javaでは、ポインタはデフォルトでnullを指すし、メモリ解放をプログラマが明示的に行うことがないので)。彼が僕の提案に同意したのは、「CでやってきたのだからJavaでもやるべきだ」ということを意味していたようだった。