これからポインタについて見ていくわけですが、そのためにもまず、参照をしっかりと憶えてください。参照をマスターすることで、ポインタの理解がダントツに早くなります。それはもう、ポインタだけを勉強するよりもずっとです。 |
ローカル変数のスコープ
最初に、普通の変数がどのようにして作製され、破棄されるのか見てみましょう。 変数が宣言されると、メモリのスタックと呼ばれる領域に変数のサイズ分(例えばintなら32ビット=4バイト)の領域が確保されます。 作製された変数は、それ以降で「中カッコ({)の数−閉じ中カッコ(})の数<0」になったときに破棄され、使えなくなります。 また、このようにして作製された変数は、他の関数では使用できません。つまり、ソースコードを見て、変数の宣言された中カッコの中からしか、変数へとアクセスできないというわけです。
このように、ある一定区間(「スコープ」と呼びます)の中でのみアクセス可能な変数を「ローカル変数」といいます。
ローカル変数と対照的に、まず最初に作製され、そしてどこからもアクセス可能な変数もあります。ソースコードの、すべてのネストの外で宣言された変数のことで、これをグローバル変数と呼びます。
ローカル変数は非常に狭い区域からのみアクセス可能です。対してグローバル変数はあらゆる場所からアクセス可能です。 |
バケツリレー
バケツリレー(一般に「値渡し(ねわたし)」と呼ばれる方法)は、変数間でデータをコピーすることでスコープの境界を超えます。 では、右図のプログラムを追って、変数のスコープと、その中のデータの「バケツリレー」を見てみましょう。
まず、Func()でi1が宣言され、メモリ領域がスタックに作製されます。この変数のスコープは次の閉じ中カッコ、つまりFunc()の中だけです。そのため、その隣にあるTestFunc()からはアクセスできません。
そこで「バケツリレー」です。i1をTestFunc()の引数に渡して、データをバケツリレーします。
データを渡したあと、TestFunc()が実行されます。この中からはp_i1にはアクセスできますが、スコープの外にあるi1にはアクセスできません。
そこで、「戻り値」を使ってバケツリレーを継続します。 |
最悪のバケツリレー
ところが、このバケツリレー方式には3つの点で問題があります。 まずひとつは、同じ意味の変数が複数作製されるということです。上の例では、i1とp_i1は同じ値を格納し、意味的に同じ変数です。が、実際には別の変数です。つまり「同じ目的に使っている変数が複数存在する」ということです。 バケツリレー方式の場合、こういった複数の変数が持つデータが、最終的に一致するようプログラマーは努めなければなりません。p_i1を返し忘れただけで、ふたつの変数の整合性はなくなります。こういった部分に常に気を付ける必要があります。 もしこれが、ひとつの変数だけを操作できたらとても楽になります。つまりp_i1など作らずに、TestFunc()からi1を直接利用できたら、整合性など気にせずに済むというわけです。returnなどしなくても、データは確実に反映されているのですから。
もうひとつの問題点は、複数の変数で値を返せないということです。引数はいくらでも増やせるので「関数へ入れる」のは問題ないのですが、関数が終わったあと「関数から戻す」のはreturnを使ってのひとつだけしか返せません。また、エラー処理等でreturnを値の返還に使用したくない場合もあります。
最後の問題点は、データのコピーが大変ということです。intのように、複雑でもなく大きくもない変数なら問題はありません。が、構造体や配列といった複雑で大きい変数は、コピーに時間がかかったり特殊な処理を行わなければならなかったりします。その最も顕著な例はクラスです。例えば次のように、intをCStringに置き換えてみます。 |
void Func()
{
CString cStr = "おえいう";
cStr = TestFunc( cStr );
}
CString TestFunc( CString p_cStr )
{
p_cStr += "あ";
return p_cStr;
}
このコードを、順を追って見ていきます(ちょっと図が小さいので見づらいかもしれません)。
クラスには「コンストラクタ」と「デストラクタ」と呼ばれる特殊な関数を持ちます。前者は「クラス型の変数が宣言されたとき」に、後者は「同じく破棄されたとき」に呼び出される関数です。値渡しを使用すると、このふたつの関数が何度も呼び出されてしまうのです。
まず、データを渡す引数が作製され、このときにコンストラクタが呼び出されます(正確には「コピーコンストラクタ」という、データのコピー専用のコンストラクタです)。
値を返すためのバケツリレーが終わったあと、関数内の変数がすべて削除され、このとき引数p_cStrのデストラクタも呼び出されます。
まとめると、引数のコンストラクタ−>一時オブジェクトのコンストラクタ−>引数のデストラクタ−>一時オブジェクトのデストラクタという順番でコンストラクタとデストラクタが呼ばれてしまうのです。これは、時には大きな負担になってしまいます。 |
「バケツリレー」に代わるもの、「ホース」
結局「バケツリレー」は、スコープの外の変数にアクセスできないために使用せざるを得なくなっています。かといって、グローバル変数ではアクセスし放題のため、管理が大変になってしまいます。 もし「ホース」があったらどうでしょう。水源から伸びるホースなら、ホースの先からしか水は出ません。ホースの一方から水を送り、また水を戻す。ホースの先だけを管理すれば、脇から水を横取りしたりはできませんし、バケツで水を受け渡す手間もなくなります。バケツリレーの問題が解決するわけです。 このホースこそがC++の参照です。次回、このホースの使い方をじっくり見てみましょう。 |
(C)KAB-studio 1998 ALL RIGHTS RESERVED. |