関数
詳説C++【第2版】標準C++完全理解を読んだときに書いたメモの一部
宣言位置の流儀
- 基本は,ブロッグの最初に宣言と初期化を一括して行う.
- メモリを動的に確保する場合は,動的確保の必要が生じた場所でポインタの宣言,動的確保,インスタンスの初期化を行う.
- for文の中だけで使用するカウンタ変数は,for文頭部で宣言・初期化を行う.
大域変数の参照
局所的な識別子の名前は,大域的な識別子の名前を隠してしまう.
つまり,大域変数gが存在するとき,ある関数内でも局所変数gを宣言すると,呼び出し時に優先されるのは局所変数であるgの方である,ということ.
そのため,大域識別子を参照するときは,常に大域識別子を::演算子付きで記述するようにすべき.
関数による実引数を渡す2つの方法
- 値による呼び出し.
仮引数の値を操作しても,実引数の値には影響しない. - 参照による呼び出し.
仮引数への操作がそのまま実引数への操作になる.
リファレンス
仮引数が実引数の別名同体のように働いて,仮引数への操作がそのまま実引数への操作になるような方式.
C言語ではサポートされていなかった(その代わり,アドレス渡しであるポインタがあった).
リファレンスの宣言
型T1& リファレンス名 = T1型の変数名 or 関数名;
- リファレンスの配列やvoid&型の配列は作れない.
- リファレンスの値の参照・変更は,オリジナルの値の参照・変更と等価.
- リファレンスのアドレスを取ると,オリジナルのアドレスが得られる.
- リファレンスのリファレンスは,オリジナルのリファレンスと等価.
- 必ず,オリジナルを指定して初期化しなければならない.
注意点
- 一般的に,charやintといったCやC++に元から備わっている型に関しては値渡しの方が効率が良い.
- 構造体のようなサイズの大きい型の場合はアドレス渡しのほうが効率的.
- 作業用無名インスタンスが生成される場合は,比較的に低速で,メモリも多く使用する.
作業用無名インスタンス
T2 c; const T1& cr = c;
上記のような宣言をしたときに生成される.
この時,T2は,T1に変換できる型でなければならない(ex, T2:double,T1:intなど).
この場合,cの型がT2からT1に変換されて,T1型の作業用無名インタンスに代入される.
標準的な引数の渡し方
実引数の値を変更するか? ⇒ NO : 実引数の型のサイズが多きいか? ⇒ NO : 値渡し ⇒ YES : const リファレンス渡し ⇒ YES : 実引数の型が組込み型か? ⇒ NO : 非const リファレンス渡し ⇒ YES : アドレス渡し ------------------------------------------------------------ constリファレンス : const T& cr = c; のように宣言したもの ------------------------------------------------------------
この方法を採用すると,次にような関数呼び出しがあったとき,
(1) f( &a ) (2) f( a )
(1):変数aは変更される可能性がある.
(2):変数aは変更されないと判断できる.
しかし,組み込み型でもリファレンス渡しを採用すると,(2)のケースでも変数aの値が変更されるケースが出てきてこの考えが破綻する.
そのため,組み込み型は別.
配列の使用
アドレス渡し void f( char b[6] ){ ... } void f( char* b ){ ... } リファレンス渡し void f( (&b)[6] ){ ... } void f( (&b)[] ){ ... } // エラー
リファレンスの返戻値
関数の返戻値として,リファレンスを使う場合,次のようにする.
T& 関数名( ... ){ ... }
関数の局所変数へのリファレンスを返さないように注意が必要.つまり,
int& f() { int a = 0; return a; }
このようにしてはいけない,ということ.
f()の実行が終了すると,f()は解放されてしまい,メモリ上に存在しないものへの参照となってしまうから.
デフォルト仮引数
- デフォルト実引数のなかでは局所変数を用いることはできない.
- 仮引数リストの一番右の仮引数から左の仮引数に向かって,順番にデフォルト実引数を設定しなければならない.
- 関数呼び出しのときに引数を省略できるのは一番右から.
- デフォルト実引数を指定するときには,プロトタイプ宣言の中で行うのが一番良い方法.
- 関数の多重定義と併用してはならない.