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 アップキャストとダウンキャスト >
『につづく!』
「ポインタの世界は、普通の世界と違うんです」
『納得いかねー!!』