ろくに使わない引数にいちいちいつもお決まりの値を渡し続けるのも面倒です。こういうとき、ちょっと怠けたいと思います。実は、怠けられます。今回はその方法について話します。
では、要点です。
では、いってみましょう。
第49章で作った文字列のコピー関数 strcpy_ex を思い出して下さい。ビット演算を使わない方のです。
// 文字列をコピーする関数 void strcpy_ex(char* pszDest, const char* pszSource, int fAdd, int fTrim, int fUpper, int fLower) { ... 略 ... } |
この関数を使うとき、普通は fAdd = 0, fTrim = 0, fUpper = 0, fLower = 0 くらいしか使わないとします。それでも、毎回毎回 0, 0, 0 ,0 という引数を書かないといけないなんて不条理ですね。でも、こんな引数なんかいらない、という訳ではなく、たまにはフラグを立てることもあるのです。
この不条理は、C言語では甘んじるしかありませんが、C++では甘んじる必要はありません。大抵同じ値の渡されるような引数には、デフォルト引数を設定することができます。設定方法は簡単です。仮引数に値を代入するように書けばいいだけです。
// 文字列をコピーする関数 void strcpy_ex(char* pszDest, const char* pszSource, int fAdd = 0, int fTrim = 0, int fUpper = 0, int fLower = 0) { ... 略 ... } |
こうすると、例えば strcpy_ex(str1, str2); と呼ぶと、省略された部分にはデフォルト引数が渡されます。つまり、strcpy_ex(str1, str2, 0, 0, 0, 0); としたのと同じ事になります。そして、strcpy_ex(str1, str2, 1, 1); と呼ぶと、strcpy_ex(str1, str2, 1, 1, 0, 0); としたのと同じ事になります。
プロトタイプ宣言(第26章参照)を行う場合は、デフォルト引数は片方のみ(普通はプロトタイプの方)にだけ書きます。プロトタイプと関数の定義の両方に書いたらエラーになります。
例) void Func(int a = 0); void Func2() { Func(); } void Func(int a) { cout << a << endl; }
さて、「そうか、略せるのか」と、strcpy_ex(str1, str2, ,1); と書きました。しかし、エラーが出てしまいました。何と、途中の引数だけを省略することはできないのです。ここは面倒でも strcpy_ex(str1, str2, 0, 1); と書かなければいけないのです。最後の引数だけを設定したいときには、結局全ての引数を書かなければいけないわけです。
そのため、使用頻度の少ないものを最後の方に持っていくとよいでしょう。
そして、「そうか、デフォルト引数が設定できるのか」と、void Func(int a, int b = a) とすると、またエラーになりました。デフォルト引数には静的なデータしか指定することができないのです。
静的なデータというのは、定数、もしくはプログラムが実行される前に位置が決まっているデータです。すなわち、定数、外部変数、関数のことです。位置的に内部変数が指定できないのはもちろんですが、この制限のため引数ですら指定することはできません。
引数は関数を呼ぶたびに位置が変わります。もちろん位置が同じ事もありますが、違うことがある以上、デフォルト引数に指定することはできないのです。
引数の位置が変わるということを、次のプログラムで確かめてみましょう。
プログラム | 実行結果例 |
---|---|
// Param1.cpp #include <iostream.h> void Func(int a = 0){ cout << &a << endl; } void Func2() { Func(); } int main() { Func(); Func2(); Func(); return 0; } |
0x0065FDA8 0x0065FD54 0x0065FDA8 |
早速デフォルト引数を使っています。便利でしょう?
0x とついているのは、16進数でしたね(第45章参照)。同じ main から呼んだときは同じアドレスにありますが、Func2 を通して呼べばアドレスが変わっていることが分かります。
このような静的でないデータは、デフォルト引数に指定することはできません。
最後に、オーバーロードとデフォルト引数を併用したときの問題点を言っておきます。
たとえば、次のような関数があったとします(プロトタイプのみしか書いていません)。
void Func(int a, int b); void Func(int a, int b = 0, int c = 0);
引数の個数が違うので、このオーバーロードは成功します。しかし、Func(0, 1); と呼ぶと、このどちらを呼び出したのか区別がつかないことが分かります。このときはエラーになります。
Func(0, 0, 1); や Func(0); と呼ぶと、区別はつき、無事関数の呼び出しは成功します。
このことから分かるように、上の Func は呼び出せません。オーバーロードが成功していながら、上の Func を使おうとするとエラーになるのです。オーバーロードとデフォルト引数を併用するときは、呼び出しの区別ができるのかきちんと確認しましょう。
では、最後に要点をまとめておきます。
次回は関数3部作の最後、インライン関数について話します。それでは、次回まで。
Last update was done on 2000.7.27
この講座の著作権はロベールが保有しています