2006-06-09
C言語のポインタには、大きく分けて以下の3つの役割があるかと思います。
- オブジェクトまたは関数に対するエイリアス
- 動的オブジェクトの保持
- 配列要素へのアクセス
これらは、それぞれ意味や目的が異なるわけですが、Cでは、意味や目的より、実現方法が同じものを同じ構文で記述する傾向にあります。それに対して、C++では、仮に実現方法が全く異なっていても、意味や目的が同じものを同じ構文で記述し、意味や目的が異なるものには別の構文を用いる傾向にあります。
そういった意味で、C++には、上記の3つそれぞれに対応した構文があります。今回は、1.のエイリアス(別名)の役割に特化した「参照型」についてお話します。なお、C++の参照型は、オブジェクトのエイリアスを作るものであって、関数のエイリアスを作るものではありません。関数のエイリアスを作るのであれば、(void(* const)()型のような)ポインタ定数を使えばよいでしょう。
それでは、C++の参照型を使った簡単なサンプルを見てみましょう。
int value = 123;
int& ref = value;
参照型は、上記のようにして宣言します。すなわち、ポインタの宣言では * を使うところを & を使うわけです。そして、参照先へのアクセスには、* などを付ける必要はなく、単に
ref とするだけで、value の内容にアクセスすることができます。
当然のことながら、ref に対して、代入やインクリメントなどの更新処理を行えば、その結果は value にも反映されますし、value を更新すれば、ref にも反映されます。
参照変数が参照するオブジェクトは、初期化のときにのみ指定することができます。ポインタの場合は後から参照先を変えることができましたが、参照型では後から参照先を変更することができません。これによって、参照型を他の目的(配列要素の順次アクセスなど)に流用することができなくなっています。
ポインタを使った間接参照とは異なり、参照型の場合は、必ずしも参照先のアドレスを用いて間接参照するわけではありません。そのため、ソースコード上では参照変数を介してアクセスしている場合でも、実際の機械語では、直接実体にアクセスしている可能性もあります。つまり、ポインタを使って参照するより、専用の機能である参照型を使った方が、より効率的なコードになる可能性があるわけです。
ところで、ポインタの場合もconst修飾付きのポインタというのがあるように、参照型の場合もconst修飾付きの参照というものがあります。次の例を見てください。
int value = 123;
const long& ref = value;
本来、参照型の初期化には、参照する型と全く同じ型か、あるいはその型からCV修飾を取り除いた型のオブジェクトを指定しなければなりません。しかし、const修飾付きの参照型の場合、上の例のように、異なる型のオブジェクトを使って初期化することができてしまいます。
const修飾付きの参照変数を、異なる型のオブジェクトで初期化しようとした場合、実際には初期化子として指定したオブジェクトを参照するのではなく、暗黙的に一時オブジェクトが生成されて、その一時オブジェクトへの参照になっていまいます。その結果、上記の例では、value を更新したとしても、ref の値は変化しません。
仮に、int型とlong型のサイズが全く同じであったとしても、型が異なれば一時オブジェクトが生成されます。Cでは、汎整数型という分類さえ同じであれば、後は型の違いはサイズの違いでしかなかったのですが、C++では、型の違いはサイズだけではなく、もっと厳格に扱われるのです。
Comment