#pragma twice

KAB-studio > プログラミング > #pragma twice > 347 Version 16.20 オーバーロードの選択

#pragma twice 347 Version 16.20 オーバーロードの選択

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

 Version 16.20
オーバーロードの選択

まず、前回問題になった点を洗い出してみます
コンストラクタか、型変換演算子のオーバーロードか、ってことだったよね
そう、この選択は非常に重要です。まず、実際に例を見てみようか。

// Data.h
class CData;

class CData2
{
public:
    // public メンバ変数。
    int m_i;

    // 引数のないコンストラクタ。
    CData2(){}
};

// CDataクラス。
class CData
{
public:
    // public メンバ変数。
    int m_iData;

    // CData2クラスへの型変換演算子のオーバーロード。
    operator CData2();
};


// Data.cpp
#include <Windows.h>
#include <stdio.h>

#include "Data.h"

// CData2クラスへの型変換演算子のオーバーロード。
CData::operator CData2()
{
    return CData2();
}

ちょっと手抜きして、CData2クラスのメンバ関数はCData2クラス内で定義
しています
まぁ何もしてないからいいんじゃない?
この例では、CDataクラスにCData2クラスへの、型変換演算子の
オーバーロードを行っています
〈CData::operator CData2()〉がそうね
この時の使用例は、以下のようになります

// 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
    )
{
    CData cData;
    cData.m_iData = 100;

    // 型変換演算子のオーバーロードを呼び出します。
    CData2 cData2 = cData;
    // CData::operator CData2()

    return 0;
}

 cData 変数は CData クラスのため、 CData2 クラスにキャストが行われ
るので CData クラスの operator CData2() メンバ関数が呼び出されます
型変換演算子のオーバーロード関数が呼び出される、ってわけね
さて、この状態で、 CData クラスを持つコンストラクタを CData2 クラス
に追加してみます

// Data.h
class CData;

class CData2
{
public:
    // public メンバ変数。
    int m_i;

    // 引数のないコンストラクタ。
    CData2(){}
    // CData クラスを引数に持つコンストラクタ。
    CData2( CData &p_rcData )
    {
        OutputDebugString( "CData2( CData &p_rcData )\n" );
    }
};

// CData クラスは同じです。
// Data.cpp ファイルも同じです。

 CData2 クラスに、 CData クラスが引数のコンストラクタ追加したのね
この状態で、先ほどの例を試してみます

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    CData cData;
    cData.m_iData = 100;

    // どっちが使われる?
    CData2 cData2 = cData;
    // CData2( CData &p_rcData )

    return 0;
}

あ! 今度はコンストラクタの方が呼び出された!
このように、型変換演算子のオーバーロードではなく、コンストラクタの
オーバーロードの方が呼び出された、というわけです
そうなんだ……それじゃ、型変換演算子の方は必要ない? あ、でも

    CData2 cData2 = (CData2)cData;

とかやったら必要かな?
……実は必要じゃないんです。この時も、コンストラクタが呼び出されま

??? それはないでしょ、だってキャストなんだし
実は、キャスト時に、コンストラクタが呼び出される場合があるんです
……何それ、ちょっと待って
たとえば、以下のように、 cData 変数を CData2 クラスにキャストして、
そのメンバ変数の m_i を使用した場合

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    CData cData;
    cData.m_iData = 100;

    // どっちが使われる?
    int i = ( (CData2)cData ).m_i;
    // CData2( CData &p_rcData )

    return 0;
}

げげげげげ!! なにそれ、コンストラクタ呼び出されてる!! 
型変換演算子の方呼ばれてないし!
実は、キャスト時には〈仮の変数〉を作るという機能があるんです
仮の変数??
たとえば、 int 型と double 型
 double 型って、 Version 12.03 ( No.226 ) でやった浮動小数点って
ゆーのだよね。あれは複雑だった……
仮数部とか実数部とか説明したよね。 double 型は int 型と全然構造が
違うっていうことを思い出して
うん、思い出した
それを踏まえて……

    int i = 100;
    double d = (double)i;


という式を考えてみて。 int 型の i を、 double 型にキャストしていま

うん、キャストしてる
キャストする結果、〈 (double)i 〉は、内部では i の値 100 を double 
型に変換する、という処理が行われます
うん、全然中身が違うんだから、新しく作り直し……待って
そう、〈 (double)i 〉を行うと、 double 型の変数が新しく作られて、
その中に変換後の 100.0 が格納される、というわけです
新しく作られる! 変数が作られる、コンストラクタが呼び出される
ということ。この例で無理矢理例えるなら、〈 double::double( int ) 〉
っていうコンストラクタが呼び出されるわけです
うわー、キャストでコンストラクタが呼び出されちゃったー!!
このように、コンストラクタがキャスト代わりになってしまう場合がある
わけです
型変換演算子のオーバーロードの意味がない……
でも、もちろんコンストラクタが無ければ意味はあるよ
あ、そういえば
そして、重要な点は、コンストラクタは【変換先】になきゃいけないけど
型変換演算子のオーバーロードは【変換元】にあればいい、ということ
あ、そっか、元々あるクラスだったらコンストラクタ追加できないし
そういうこと。そういう使い分けはできるかな
でも難しそう……
実際、演算子のオーバーロードはとても難しいよ。今の話はコンストラクタ
だけど、 = 演算子のオーバーロードは関係ないでしょ
げ、そっか、そっちはキャストしたときには呼ばれないよね
でも、コンストラクタと = 演算子のオーバーロードは同じようにしない

初期化と代入が違っちゃう!
他にも、普通の関数にするのかメンバ関数にするのかとか
それもあった……
というわけで、演算子のオーバーロードはとても面倒なのです

/*
    Preview Next Story!
*/
って、そんなもん教えるなー!
だったらいいんだけど、使えないと困る場面があるんだよね
げ、そうなの?
そういう典型を次回は説明します
うーん教わりたくない
というわけで次回
< Version 16.21 演算子のオーバーロードの実例 >
につづく!
それ呼んだ後は STL & iostream 入門をどうぞ
宣伝!? それよりそれも解説してよー
それだけで何年かかるか……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。