Version 16.24
デフォルト値vsオーバーロード
「前回はデフォルト値について説明しました」
『引数にデフォルトの値を渡せるってゆーことだったね』
「で、実はこのデフォルト値と同じことが、オーバーロードでもできるんで
す」
『オーバーロードでも……あれ? もしかしてそれって引数省略するってだ
けのこと?』
「そういうこと。前回の UseDefault() 関数をオーバーロードで実現すると
以下のようになります」
// Data.h
// デフォルト値ではなくオーバーロードで実現します。
// void UseDefault( int p_i1, int p_i2 = 100 );
void UseDefault( int p_i1, int p_i2 );
void UseDefault( int p_i1 );
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
/*
void UseDefault( int p_i1, int p_i2 )
{
// 配列の方を出力します。
char pch[256];
sprintf( pch, "%d, %d\n", p_i1, p_i2 );
OutputDebugString( pch );
// 100, 101
}
*/
// デフォルト値ではなくオーバーロードで実現します。
void UseDefault( int p_i1, int p_i2 )
{
// 配列の方を出力します。
char pch[256];
sprintf( pch, "%d, %d\n", p_i1, p_i2 );
OutputDebugString( pch );
// 100, 101
}
void UseDefault( int p_i1 )
{
// 第2引数にはデフォルト値を渡します。
UseDefault( p_i1, 100 );
}
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// デフォルト値を使用します。
UseDefault( 100 );
// 100, 100
// デフォルト値を使用しません。
UseDefault( 200, 300 );
// 200, 300
return 0;
}
「まず、引数が1つの関数と2つの関数の両方作ります」
void UseDefault( int p_i1, int p_i2 );
void UseDefault( int p_i1 );
「これでオーバーロードできました。引数2つの方は前回の実装と同じ、引数
1つの方は引数2つの方にデフォルト値を渡して呼び出します」
void UseDefault( int p_i1 )
{
// 第2引数にはデフォルト値を渡します。
UseDefault( p_i1, 100 );
}
『なるほど、そうすれば前回のとまったく同じになるってわけね』
「そういうこと。こうすることで、ほぼデフォルト値と同じことを
オーバーロードで実現できるというわけです」
『ほぼ同じ?』
「前回の説明を思い出して。デフォルト値は、関数の宣言部で記述したで
しょ」
『あ、今回は定義の中だね』
「その違い。 Version 15.02 ( No.302 ) で説明したように、関数の宣言は
〈こういう関数がある〉という情報でしかありません」
『実際に結び付けるのはリンクの時だもんね』
「だから、 Data.h をインクルードせず、以下のようにすることだって
できるんです」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
// デフォルト値を持つ、 UseDefault() 関数の宣言。
void UseDefault( int p_i1, int p_i2 = 200 );
// 引数のデフォルト値を持つ関数。
void UseDefault( int p_i1, int p_i2 )
{
// 配列の方を出力します。
char pch[256];
sprintf( pch, "%d, %d\n", p_i1, p_i2 );
OutputDebugString( pch );
// 100, 101
}
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// デフォルト値を使用します。
UseDefault( 100 );
// 100, 100
// デフォルト値を使用しません。
UseDefault( 200, 300 );
// 200, 300
return 0;
}
『あ、こっちはデフォルト値の例だね』
「この例では、 Data.h をインクルードせず、勝手に UseDefault() 関数の
宣言を書いて、勝手にデフォルト値を指定しています」
// デフォルト値を持つ、 UseDefault() 関数の宣言。
void UseDefault( int p_i1, int p_i2 = 200 );
『げ、ホントだ』
「でも、これができるのも仕方ないんです。関数宣言というのは、関数を使
う側にとっての情報だから」
『好きにしていいってこと?』
「そういうこと。呼び出す側にとって、ヘッダーファイルは〈インクルード
した方が便利〉だからインクルードするだけだからね」
『つまり?』
「つまり、呼び出される側に強制力はない、ってこと」
『そか、つまりデフォルト値は変えられるかもしれない、強制できないって
ことね』
「そういうこと。デフォルト値はいわば〈呼び出す側が使用する、省略した
時に渡す値〉。たとえば」
// デフォルト値を使用します。
UseDefault( 100 );
「としたら、これが呼び出すときに」
// デフォルト値を使用します。
UseDefault( 100, 200 );
「となる、という話なんです」
『その値を宣言で指定している、と』
「そしてそれは呼び出す側で自由に変えられる、ってことは結局普通に渡し
ているのと同じ。プログラマーにとって楽、というだけのことなんです」
『で、オーバーロードの方は』
「デフォルト値は定義内で渡すから、呼び出す側で変えることはできません。
しかも、何を渡すかも分からないかもしれません」
『あ、そういえばそうだよね、 DLL になってたら分かんないよね』
「それに、この例では引数2つの方を呼び出しているけど、呼び出さないか
もしれません」
『あ、そういえば……そか、オーバーロードの時は、引数が減ってるだけ
じゃなく関数が別っていうのもあるんだ』
「それも重要な点。デフォルト引数は関数は1つだけどオーバーロードは2つ
以上あるわけだから、中身は全然違うかもしれないんです。それに、関数が
多いってことは、それだけ関数を作らなきゃいけないってことでもあるんで
す」
『げ!』
「引数が5つあって、そのうち4つ省略したい場合、デフォルト値なら1つの
関数で済むけど、オーバーロードの場合は」
『4つ作らなきゃいけない!』
「ただ、デフォルト値を設定しない引数を、設定した引数の後に入れること
はできないんです。たとえば」
void UseDefault( int p_i1, int p_i2 = 100, int p_i3 );
「っていうのは駄目」
『あー、じゃあ引数の一部だけ省略するのはデフォルト値じゃ無理なんだ』
「オーバーロードならその辺は柔軟にできるし、あと呼び出す側で違いを把
握できないっていうのは、オーバーロードは全てそうだから」
『?』
「たとえば int と double のオーバーロード」
『あ! そか、型が違うオーバーロードでも、同じように動くようにしな
きゃいけないって言ってたよね』
「 Version 11.19 ( No.219 ) で説明したね。引数を省略する時も同じ。加
えて、宣言部の記述に関係なく正しい値が渡される、ってことを考えると
デフォルト値よりオーバーロードの方がお勧めかな」
『って、関数いっぱい作らなきゃいけないって問題は?』
「努力でカバー」
『……』
/*
Preview Next Story!
*/
『 C++ って、似たような機能のって多いよね』
「その中から選択しなきゃいけないから大変かな」
『だよね、メリットデメリット考えないといけないんだもん』
「次の static メンバ変数もそんな感じかな」
『またー?』
「というわけで次回」
< Version 16.25 static メンバ変数 >
『につづく!』
「本当の理解が必要なのが C++ 言語!」
『理解しなくても使える言語があるの?』
「うーん……難しい質問だねそれは」