Version 16.23
引数のデフォルト値
「今回は引数のデフォルト値について説明します」
『演算子のオーバーロードからうってかわって、って感じだね』
「ううん、実はとても関係があるんです」
『え?』
「まずは実例から。引数のデフォルト値は普通の関数でもメンバ関数でもで
きるから、普通の関数で試してみます」
// Data.h
void UseDefault( int p_i1, int p_i2 = 100 );
// Data.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
}
「 UseDefault() の第2引数で 100 というデフォルト値を使用しています」
『【 int p_i2 = 100 】がそうね』
「使用例は以下のようになります」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
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;
}
「最初の方は、デフォルト値を使用して呼び出しています」
// デフォルト値を使用します。
UseDefault( 100 );
// 100, 100
「このように、引数を省略するとデフォルト値が使用されます。つまり、
p_i2 に 100 が渡されて呼び出される、ということです」
// デフォルト値を使用します。
UseDefault( 100 /* 第2引数を省略 */ );
↓ ↓
↓ ↓
↓ →→→→→
→→→ ↓
↓ ↓
void UseDefault( int p_i1, int p_i2 = 100 );
↓ ↓
↓ ←←←←
↓ ↓
void UseDefault( int p_i1, int p_i2 )
{
// 略
『省略すると、その値が渡されるってわけね』
「もちろん、デフォルト値を使用しないこともできます」
// デフォルト値を使用しません。
UseDefault( 200, 300 );
// 200, 300
『だからデフォルト値、なんだもんね』
「そういうこと」
『……質問!』
「はい火美ちゃん」
『これって宣言の方だけでいいの? 定義の方はいらないの?』
「そこが複雑なところなんです。たとえば、以下のようにデフォルト値の
設定を、定義の方だけしたとします」
// Data.h
void UseDefault( int p_i1, int p_i2 );
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// 引数のデフォルト値を持つ関数。
void UseDefault( int p_i1, int p_i2 = 100 )
{
// 略
『げ、コンパイルエラーになった!』
// デフォルト値を使用します。
UseDefault( 100 );
// コンパイルエラー:
// error C2660: 'UseDefault' :
// 関数が不正な 1 個の実引数をともなって呼び出されました。
『なんでなんで!? 宣言の方に書かないといけないの?』
「宣言の方に書かなきゃいけないわけじゃなくて、コンパイルするときに
デフォルト値の情報がないといけないんです」
『???』
「コンパイラのことを思い出して。まず、コンパイルはソースファイル単位
で行うよね」
『 Version 15.01 ( No.301 ) とかで教わったね』
「コンパイラは、 Main.cpp をコンパイル中に、以下の文にでくわします」
// デフォルト値を使用します。
UseDefault( 100 );
「コンパイラは、この UseDefault() 関数の引数と戻り値の情報を取得しよ
うとします。その情報はどこに書かれてる?」
『 Data.h と Data.cpp 』
「でも、コンパイルはソースファイル単位でしょ」
『あ、そっか。 Main.cpp のコンパイルしてるときには Data.cpp は見てな
いんだ。 Main.cpp からは Data.h をインクルードしてるから見えるけど』
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
「そう、だから Main.cpp のコンパイル時には Data.h の情報を持つことに
なります。その中で、以下の関数を見つけます」
// Data.h
void UseDefault( int p_i1, int p_i2 );
『デフォルトの引数使っていないバージョンね』
「コンパイラは、呼び出し元の引数が1つだけなのに、宣言の方は引数が2つ
の関数しかないから、どの関数を呼び出せばいいのかわかりません」
『コンパイラは Data.h しか見られないから、定義の方にデフォルト値書い
ても分からない、って話?』
「そういうこと。デフォルト値はコンパイル時に使用されるので、コンパイル
するときに見に行く Data.h の宣言に書く必要があります」
// Data.h
void UseDefault( int p_i1, int p_i2 = 100 );
「このようにすることで、コンパイラは呼び出し部分にデフォルト値を渡す
ようにします」
// デフォルト値を使用します。
UseDefault( 100, 100 );
// ↑デフォルト値
『呼び出すとこにデフォルト値を渡しちゃう渡すわけね』
「このように、デフォルト値はコンパイル時に処理されるので、宣言に書く
必要があります」
『コンパイラから見えなきゃいけないわけね』
「ただ、定義に書いてもいい場合もあります」
『定義に書いてもいい場合?』
「定義しかない場合とかね
// Main.cpp
#include <Windows.h>
#include <stdio.h>
// 引数のデフォルト値を持つ関数。
void UseDefault( int p_i1, int p_i2 = 100 )
{
// 配列の方を出力します。
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;
}
『あー、定義ファイルにしかない場合は仕方ないよね』
「どちらにしろ、コンパイラがデフォルト引数が得られればいい、ってこ
とだから」
/*
Preview Next Story!
*/
『あれ? オーバーロードと関係してるって話は?』
「引数を省略、ってオーバーロードでもできるでしょ」
『そういえば、ってここで言われても』
「じゃあ1回そのために取りましょう」
『げ』
「というわけで次回」
< Version 16.24 デフォルト値vsオーバーロード >
『につづく!』
「こうしてどんどん伸びていく」
『自分で言うなー! もう7年だよー!?』