Version 17.06
継承と代入
「前回は MFC 全般の継承関係について説明しました」
『これまで結構継承って使ってたんだね、あまり意識したことなかったけ
ど』
「 MFC はそういうところあまり意識しなくても使えるようにしてあるから
ね。さて、今回はちょっとややこしい、継承関係にあるクラスの代入につい
て説明します」
『その説明がもうややこしいんだけど』
「どういうことかというと」
・基本クラス←派生クラスの代入
・派生クラス←基本クラスの代入
「ができるかどうか、できるとしたらどうなるのか、っていう話」
『あーなるほど、ならそんなに複雑じゃなさそうだけど』
「まぁ実は、これがすっごーく複雑な話に継ながるんだけど」
『げげ』
「ま、とりあえずは分かりやすいところから。分かりやすくするため、
public メンバ変数を使います」
// Data.h
// CData クラス。
class CData
{
public:
int m_iData;
};
// CData クラスの派生クラス。
class CDerivedData : public CData
{
public:
int m_iData2;
};
『ただメンバ変数持ってるだけのクラスね』
「基本クラスは 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 cData;
cData = cDerivedData;
// 出力します。
char pch[256];
sprintf( pch, "%d\n", cData.m_iData );
OutputDebugString( pch );
// 100
return 0;
}
『んと、ここでしてるんだよね』
基本 ← 派生
cData = cDerivedData;
「そう。このように派生クラスから基本クラスに代入すると、派生クラスの
各メンバ変数の値が基本クラスの各メンバ変数に代入されます」
// 出力します。
char pch[256];
sprintf( pch, "%d\n", cData.m_iData );
OutputDebugString( pch );
// 100
『お、本当に入ってる』
「つまり、さっきの代入は、以下のように置き換えたのと同じってこと」
cData = cDerivedData;
↓↓↓
cData.m_iData = cDerivedData.m_iData;
『あれ? m_iData2 は?』
「代入されません。だって、 CData クラスにはないから」
『あー……つまり、基本クラスにあるメンバ変数だけコピーされる、と』
「そういうこと。図にするとこうかな」
┌──────┐ ┌───────┐
│ cData │ │ cDerivedData │
│ │ │ │
│ m_iData ←←←←←←m_iData │
└──────┘ │ m_iData2 │
└───────┘
『 cData にない 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
)
{
// CData クラスを用意します。
CData cData;
cData.m_iData = 100;
// CDerivedData クラスを用意して代入します。
CDerivedData cDerivedData;
cDerivedData = cData;
// コンパイルエラー:
// error C2679: 二項演算子 '=' :
// 型 'class CData' の右オペランドを扱う演算子は定義
// されていません。(または変換できません)
return 0;
}
『なんですと、コンパイルエラー!? 代入できないってこと??』
「そういうこと。つまり」
・派生クラス←基本クラスの代入はできない
「ということです」
『な、なんで???』
「なぜかというと、派生クラスには基本クラスにないメンバ変数があるから」
『 m_iData2 があるから、ってこと?』
「そういうこと。代入は〈右の値を左の変数に入れる〉わけだから、
m_iData2 のような〈入れる元の値〉がないと代入できないわけです」
『むむむ?』
「図にするとこう」
┌───────┐ ┌──────┐
│ cDerivedData │ │ cData │
│ │ │ │
│ m_iData ←←←←←← m_iData │
│ m_iData2 ? │ └──────┘
└───────┘
「こんな感じに、m_iData メンバ変数は cData にも存在するからいいんだ
けど、 m_iData2 は cData にないから、何を入れたらいいかわからないわ
け」
『 m_iData2 が宙ぶらりんになっちゃう、だから代入できない……と』
「そういうこと。だから、基本クラスから派生クラスへは代入できないんで
す」
『質問!』
「はい火美ちゃん」
『それってつまり、 CDerivedData クラスから m_iData2 をなくせばいいっ
てこと?』
「ううん、今の説明ではとりあえずそう説明したけど、実際にはメンバ変数
の内容に関係なく基本クラスから派生クラスへは代入できないから」
『むむむ?』
「派生クラスは、基本クラスに〈メンバが加えられたもの〉だから、ってい
う前提で基本クラスから派生クラスへの代入が禁止されているんです」
『んー、なんか理不尽な気もするけど……もひとつ質問!』
「はい火美ちゃん」
『この例だと、 m_iData だけは代入できるでしょ。それだけ代入するのっ
てどうするの?』
「そういう場合はひとつずつ代入します」
『げげ』
// CDerivedData クラスを用意して代入します。
CDerivedData cDerivedData;
cDerivedData.m_iData = cData.m_iData;
cDerivedData.m_iData2 = 0;
『めんどくさー。ねぇ、キャストはできないの?』
「できません」
// CDerivedData クラスを用意して代入します。
CDerivedData cDerivedData;
cDerivedData = (CDerivedData)cData;
// コンパイルエラー:
// error C2440: 'type cast' :
// 'class CData' から 'class CDerivedData' に変換することは
// できません。
『う”ー』
「まぁこれは仕方ないかな。それに、実際問題として派生クラスから
基本クラスへもちょっと危険だから、本当はコピーコンストラクタを作った
方が安全かな」
『コピーコンストラクタって Version 16.11 ( No.338 ) のだよね。それも
めんどい……』
/*
Preview Next Story!
*/
『む、なんだか突然ハードな気がします!』
「うん、ここからは結構複雑かも」
『なんですと!』
「でもそのあとには、とっても面白いものが待っています!」
『うわぁすごくうさんくさい!』
「う、うさんくさい!?」
『というわけで次回』
< Version 17.07 継承と代入・ポインタの場合 >
「につづく!」
『それが面白いものなの?』
「ううん、まだ複雑怪奇な話」
『うわー』