#pragma twice

KAB-studio > プログラミング > #pragma twice > 219 Version 11.19 オーバーロード関数を作ってみよう

#pragma twice 219 Version 11.19 オーバーロード関数を作ってみよう

前のページへ 表紙・目次へ 次のページへ

 Version 11.19
オーバーロード関数を作ってみよう

「今回はオーバーロードについて見てみます」
『オーバーロードって前にやったね』
「 Version 8.11 ( No.153 ) だね。オーバーロードそのものについては
そっちで解説してるからいいとして、今回は」
『作ってみる?』
「そゆこと。まずは簡単な例を見てみます」

// オーバーロードしたメンバ関数を持つクラス。
class CHasOverload
{
public:
    // メンバ関数1。
    void Overloaded()
    {
        TRACE( "Overloaded()\n" );
    }

    // メンバ関数2。
    void Overloaded( int p_i )
    {
        TRACE( "Overloaded( %d )\n", p_i );
    }
};

『 Overloaded() ってメンバ関数がふたつあるから、これがオーバーロード
してるんだね』
「そう。戻り値が同じで、引数が違うメンバ関数が、オーバーロードされた
メンバ関数。これの使用例はこんな感じ」

void Use_CHasOverload()
{
    CHasOverload cHasOverload;
    cHasOverload.Overloaded();
    // Overloaded()
    cHasOverload.Overloaded( 100 );
    // Overloaded( 100 )
}

『引数があるのとないのとで別のが呼ばれるわけね』
「これは一番わかりやすい例。引数の数が違うから、どっちが呼ばれるかっ
ていうのは一目瞭然だからね」
『そか、引数の数が同じで、型が違うっていうのもオーバーロードなんだ』
「というわけでその例」

// オーバーロードしたメンバ関数を持つクラス2。
class CHasOverload_SameType
{
public:
    // メンバ関数1。
    void Overloaded( LPCTSTR p_pch )
    {
        TRACE( "Overloaded( %s )\n", p_pch );
    }

    // メンバ関数2。
    void Overloaded( int p_i )
    {
        TRACE( "Overloaded( %d )\n", p_i );
    }
};

『上は文字列で、下は整数ね』
「使用例はこんな感じ」

void Use_CHasOverload_SameType()
{
    CHasOverload_SameType cHasOverload;
    cHasOverload.Overloaded( "テスト" );
    // Overloaded( テスト )
    cHasOverload.Overloaded( 100 );
    // Overloaded( 100 )
}

『なるほど、文字列を渡すか、数値を渡すかでどっちの関数が呼ばれるかが
決まるわけねー、これは便利かも』
「ただ、この自動的に決まる、っていうのは実は危険な面もあるんです」
『危険?』
「たとえば、文字列が NULL だった場合」

void Use_CHasOverload_SameType()
{
    CHasOverload_SameType cHasOverload;
    cHasOverload.Overloaded( NULL );
    // Overloaded( 0 )
    cHasOverload.Overloaded( 100 );
    // Overloaded( 100 )
}

『……あれ? もしかして両方とも int の方が呼ばれてる?』
「そうなんです。  Version 5.11 ( No.076 ) で教えたように、 NULL は数
字の 0 だから int の方が呼ばれちゃうんです」
『え?  NULL ってアドレスの 0 ってことじゃなかったの?』
「さっきの No.076 でも触れてるけど、 NULL って、ホントの数字の 0 な
んです」
『えーでもさ』

    int *pi = 0x10000000;

『みたいに、ポインタにアドレスの数字って直接入れられないよね』
「うん、ダメだよ、コンパイルエラーになるから。ところが、実は〈 0 だ
けはどんなポインタにも入れることができる〉っていう特例があるんです」
『ず、ずるい……』
「確かにね。だからこそ、上のような問題が出てくるわけ」
『あ、やっぱ問題なんだ』
「そう。だから、本当は数字と文字列でオーバーロードするのは危険なこと
なんです。結構多いけどね」
『多い?』
「 CString でもしてるし」
『げ』
「 No.076 で〈 NULL じゃなくて 0 って書く人もいる〉のは、そういう紛
らわしさをできるだけ取り除くためなんです」
『そか、上の例でも NULL じゃなくて 0 ならわかるもんね』
「あと、オーバーロードしてる関数は、あまり動作を変えないようにするの
がコツかな」
『どーゆーこと?』
「上の例だとどっちも TRACE() で出してるだけだけど、これがもし、片方
は TRACE() でもう片方はファイルに出力してたら」
『げ、それはヤダね』
「だから、オーバーロードは〈引数の型が違っても同じ結果になる〉ってい
う場合にするのがいいと思うよ」
『結果が違う場合は?』
「メンバ関数の名前を変えればいいでしょ」
『あ、そっか』
「最後に、オーバーロードした関数の戻り値について。オーバーロードした
時にはそれぞれ別の戻り値の型にすることができます」

// オーバーロードしたメンバ関数を持つクラス3。
class CHasOverload_OtherReturn
{
public:
    // メンバ関数1。
    void Overloaded( LPCTSTR p_pch )
    {
        TRACE( "Overloaded( %s )\n", p_pch );
    }

    // メンバ関数2。
    int Overloaded( int p_i )
    {
        TRACE( "Overloaded( %d )\n", p_i );
        return p_i;
    }
};

『片方は void でもう片方は int ね』
「使う場合にはこんな感じ」

void Use_CHasOverload_OtherReturn()
{
    CHasOverload_OtherReturn cHasOverload;
    cHasOverload.Overloaded( "テスト" );
    // Overloaded( "テスト" )
    int i = cHasOverload.Overloaded( 100 );
    // Overloaded( 100 )
    TRACE( "%d\n", i );
    // 100
}

『もし文字列の方で int を受け取ろうとしたら?』
「そうするとコンパイルエラーになります」

    int i = cHasOverload.Overloaded( "テスト" );
    // error C2440: 'initializing' : 
    // 'void' から 'int' に変換することはできません。
    // void 型の式は他の型へ変換できません。

『へー』
「これはとっても重要なこと。実は、どの関数が呼ばれるかは引数でしか決
められないんです。言い換えると、戻り値でオーバーロードすることはでき
ないってこと」
『?』
「たとえば」

// オーバーロードしたメンバ関数を持つクラス4。
class CHasOverload_OtherReturn_Bad
{
public:
    // メンバ関数1。
    void Overloaded()
    {
        TRACE( "Overloaded()\n" );
    }

    // メンバ関数2。
    int Overloaded()
    {
        TRACE( "Overloaded()\n" );
        return 100;
    }
};

『引数がなくて、戻り値が void と int ……あ、戻り値でオーバーロード
ってこういうことね』
「これをビルドしてみると」
『げ、コンパイルエラー出まくり』

error C2556: 
'int __thiscall B1::CHasOverload_OtherReturn_Bad::Overloaded(void)'
 : オーバーロード関数の戻り値は異なっていますが、
引数リストは同一です。

「というわけで、戻り値だけ変えたり、戻り値でどの関数を呼ぶか決めさせ
たりっていうのはできません」
『ちょっと不便かも〜』

/*
    Preview Next Story!
*/
『便利だけど危険、って難しいね』
「これからはそういうの増えてくからね」
『むむむ』
「でもそういう世界に踏み込むと、一人前のプログラマーって言えるかも」
『い、いつのまにそんなとこまで???』
「遠回りしたけど結構身に付いてると思うよ」
『へ〜』
「というわけで次回」
< Version 11.20 演算子をオーバーロードしよう >
『につづく!』
「でもまだ半分も教えてないけど」
『げ!』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。