#pragma twice

KAB-studio > プログラミング > #pragma twice > 362 Version 17.07 継承と代入・ポインタの場合

#pragma twice 362 Version 17.07 継承と代入・ポインタの場合

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

 Version 17.07
継承と代入・ポインタの場合

「前回は、継承関係にあるクラスの代入について説明しました」
『つまりこういうことだったよね』

○基本クラス←派生クラスの代入
×派生クラス←基本クラスの代入

「そういうこと。で、今回は似てるようで全く違う、ポインタの場合の話を
説明します」
『ポインタの場合の、継承と代入の関係ってこと?』
「そういうこと。似てるけど全然違う話だから、頭切り替えた方がいいか
も」
『そこまで言うってことはかなり複雑なんだ……』
「まず、使用するのは前回と同じ CData クラスと CDerivedData クラス。
それぞれ m_iData メンバ変数と m_iData2 メンバ変数を持っています」
『その方が分かりやすいね』
「まず、結論から言うと、基本的にはポインタの場合も以下のルールが適用
されます」

○基本クラス←派生クラスの代入
×派生クラス←基本クラスの代入

『ポインタじゃない時と同じなんだ。それなのに複雑』
「そう、その仕組みが複雑で、場合によっては〈派生クラス←基本クラス〉
が可能だから」
『げ、それは複雑だ』
「ま、その前に基本的な部分から」

○基本クラス←派生クラスの代入

// 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
    )
{
    // CDerivedData クラスを用意します。
    CDerivedData cDerivedData;
    cDerivedData.m_iData = 100;
    cDerivedData.m_iData2 = 200;

    // CData クラスのポインタを用意して代入します。
    CData *pcData;
    pcData = &cDerivedData;

    // 出力します。
    char pch[256];
    sprintf( pch, "%d\n", pcData->m_iData );
    OutputDebugString( pch );
    // 100

    return 0;
}

『ここが代入してるところだね』

    pcData = &cDerivedData;

「このように、派生クラスのアドレスを基本クラスのポインタに代入する
ことができます」
『で、それを出力するとちゃんと代入できてます、と』
「で、逆は不可です」

×派生クラス←基本クラスの代入

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

    // CDerivedData クラスのポインタを用意して代入します。
    CDerivedData *pcDerivedData;
    pcDerivedData = &cData;
    // コンパイルエラー:
    // error C2440: '=' : 
    //     'class CData *' から 'class CDerivedData *' に変換する
    //     ことはできません。
    //     指示された型は関連がありません; 変換には reinterpret_cast、
    //     C スタイル キャストまたは関数スタイルのキャストが必要です。

    return 0;
}

『こっちはここね』

    pcDerivedData = &cData;

「このように、基本クラスのアドレスを派生クラスのポインタに代入するこ
とはできません」
『ここまでは前回と同じね』
「ところが、こんなことができてしまうんです」

// 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
    )
{
    // CDerivedData クラスを用意します。
    CDerivedData cDerivedData;
    cDerivedData.m_iData = 100;
    cDerivedData.m_iData2 = 200;

    // CData クラスのポインタを用意して代入します。
    CData *pcData;
    pcData = &cDerivedData;

    // CDerivedData クラスのポインタを用意して代入します。
    CDerivedData *pcDerivedData;
    pcDerivedData = (CDerivedData *)pcData;

    // 出力します。
    char pch[256];
    sprintf
        ( pch
        , "%d, %d\n"
        , pcDerivedData->m_iData 
        , pcDerivedData->m_iData2
        );
    OutputDebugString( pch );
    // 100, 200

    return 0;
}

『……ちょ、ちょっと待った!! これおかしいって! だって』

    pcData = &cDerivedData;

『ここで CData クラスになってるんだから、 m_iData だけ代入されて、
m_iData2 は代入されないじゃん。で、』

    pcDerivedData = (CDerivedData *)pcData;

『ここで CData クラスから CDerivedData クラスにキャストしてるけど、
ってゆーか前回キャストできないって言ってたのにできてるけど、ともかく
pcData には m_iData2 がないから pcDerivedData に m_iData2 が渡される
はずないのに』

        , pcDerivedData->m_iData 
        , pcDerivedData->m_iData2
        );
    OutputDebugString( pch );
    // 100, 200

『出力できてるし! ありえねー!!』
「ここが重要な点。つまり、前回の代入とは仕組みが違うってこと」
『……どういうこと?』
「というわけで次回に続く!」

/*
    Preview Next Story!
*/
『わけわかんないし!!』
「ま、まったく別って考えるといいんだけどね」
『えー? でもキャストは同じだし』
「似てるだけ似てるだけ」
『えー???』
「というわけで次回」
< Version 17.08 アップキャストとダウンキャスト >
『につづく!』
「ポインタの世界は、普通の世界と違うんです」
『納得いかねー!!』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。