===== まずは作ったクラスの確認 =====
こんなクラスを作ってました。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:class_inheri.png?600|}}
\\
継承のサンプル\\
cBaseクラスとcCylinderクラス\\
円を表すcBaseクラスを継承して、円柱クラスcCrlinderを作ることができました。\\
しかし、インスタンスを生成して、PrintMemberを呼んでメンバ変数の値を表示しようとすると、、、\\
#include "cBase.h"
#include "cCylinder.h"
using std::cout;
using std::cin;
using std::endl;
int main()
{
cCylinder inCy(10, 5, 3.0, 5.0);
//中心(10,5) 半径 3.0 高さ5.0の円柱
inCy.PrintMember();
return(0);
}
* 実行結果
{{:game-engineer:classes:2022:game-programing-1:second-term:12:シリンダ結果.png?200|}}
ここまでが前回までの話\\
cBaseクラスにしか、PrintMember関数がないので、せっかくcCrlinderクラスのメンバーをすべて初期化しても、cBaseクラスに含まれるメンバーしか表示されません。\\
それじゃぁ、円柱のメンバ変数の値を表示する関数を作ってみよう。みたいなノリになると思います。\\
基底クラス cBase のメンバーはprivateなものもあります。\\
ここで、ちょっと重要なこと!\\
**基底←派生の関係とはいえ別のクラスですので、派生クラスから基底クラスのメンバはpublicなものしか参照できません。**\\
つまりここでは、**「cCylinderクラスの中の、cBaseクラスのprivateメンバーにはアクセスできない」**ということがわかります。\\
==== それじゃぁどうしよう ====
こんなのはどうでしょうか。
{{:game-engineer:classes:2022:game-programing-1:second-term:12:keisho2.png?600|}}
cCylinderクラスにPringMember関数を作ってみます。\\
どうなるでしょうか?名前が同じだからエラーになる❔\\
なのでとりあえず、新しく作る\\
void cCylinder::PrintMember() //メンバ関数(公開)
は、とりあえずcCylinderクラス自身のメンバ変数だけ表示することにします。\\
(ちゃんと計画的にゲッター、セッター作らないからこういうことになります8-))\\
=== void cCylinder::PrintMember()の追加 ===
#pragma once
#include "cBase.h"
class cCylinder :
public cBase
{
double height;
double volume;
void SetVolume();
public:
cCylinder()
:cBase(),height(0),volume(0)
{ cout << "円柱爆誕" << endl; }
cCylinder(int _x, int _y, double _r, double _h)
:cBase(_x, _y, _r), height(_h), volume(0) //Volumeは0にして後で計算
{
cout << "円柱爆誕" << endl;
SetVolume(); //Volumeを計算してセット!
}
//これを追加します!
void PrintMember(); //メンバ関数(公開)宣言のみ
};
#include "cCylinder.h"
void cCylinder::SetVolume()
{
//volume = area * height; //これはできない! areaはベースのプライベートメンバだから
volume = GetArea() * height;
}
void cCylinder::PrintMember()//メンバ関数(公開)
{
cout << "高さ : " << height << endl;
cout << "体積 : " << volume << endl;
}
=== 実行してみる ===
#include
#include "cBase.h"
#include "cCylinder.h"
using std::cout;
using std::cin;
using std::endl;
int main()
{
cCylinder inCy(10, 5, 3.0, 5.0);
//中心(10,5) 半径 3.0 高さ5.0の円柱
inCy.PrintMember();
return(0);
}
さて、どうなるでしょうか?\\
* 実行結果
{{:game-engineer:classes:2022:game-programing-1:second-term:12:result2.png?200|}}
なんかよくわからないですが、思った通りに動いています(汗\\
==== 考察 ====
もっとクソ簡単な例で考えてみます。\\
using namespace std;
/********** 基本クラス **********/
class cBase {
public:
void hello() {
cout << "Hello, Im Base!" << endl;;
}
};
/********** 派生クラス **********/
class cSubClass : public cBase {
public:
void hello() {
cout << "こにゃにゃちは" << endl;
}
};
int main() {
cSubClass subclass;
subclass.hello();
return(0);
}
基本クラス(cBase)も、派生クラス(cSubClass)もメンバはpublicメンバのhello()しかありません。\\
つまり、\\
基本クラスと、派生クラスで同じ名前のメンバ関数を定義しても、エラーにはならない\\
**派生クラスのインスタンス**から、その関数を呼ぶと、同じ名前にもかかわらず専ら**派生クラスのメンバ関数**が呼ばれる。\\
つまり、内部にあるはずのcBase::hello()は、cSubClass::hello()によって隠されている。状態である。\\
=== 実際に見てみよう ===
{{url>https://replit.com/@youetsusato/Yin-Bi-Guan-Xi?v=1&embed=true#main.cpp 800,800}}
cBaseクラスとcCylinderクラスに戻ってみます。\\
継承関係は [cBase]←[cCylinder]となっています。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:keisho2.png?600|}}
前に出したcBaseとcCylinderの継承の図
このとき、cCylinderクラスのインスタンス(変数)を生成すると、アクセスできるcCylinderクラスのメンバ変数、メンバ関数は以下の図のようになります。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:cylinder_class.png?400|}}
インスタンスからアクセスできるpublicメンバ
一応、列挙すると\\
; cBaseクラス\\ (継承によりcCylinder内に含まれている)
: double r
: cBase()
: cBase(int _x, int _y, double _r)
: ~cBase()
: void PrintMember()
: double GetArea()
; cCylinder
: cCylinder()
: cCylinder(int _x, int _y, double _r, double _h)
: void PrintMember()
状況を整理します。\\
- インスタンスを生成するのはcCylinderクラス
- cCylinderはcBaseを継承している
- よって、cCylinderクラスの中にcBaseクラスのpublicメンバがあるように見える
- 逆に言うと、使う側(ユーザ)は、cCylinderクラスの中にcBaseがあるとか意識しない
- よって、ユーザが見えるメンバはcCylinderの中で宣言されたのか、継承されたものなのかは気にする必要はない
- と言うか、気にしないで使うだろうねぇ
- 同じ名前のメンバがいると、使う側は、外側(Ccylinderクラス)のメンバが優先で見える
- cBase::PrintMember()とcCylinder::PrintMemberがいる気がしているのは、クラス作成側だけ
- だって使う方は見えるのだけ使うもん
という状況です。 \\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:inpei.png?400|}}
メンバの隠蔽
==== 結論(Conclusion) ====
* 継承元と継承先で、同じ名前のメンバがいると、継承先クラスのインスタンスからは、継承先のメンバが見える
* つまり、継承先のメンバで、継承元のメンバを隠してしまっている
* これを隠蔽関係という
* 隠蔽は、メンバ変数でも、メンバ関数でも起こりうる
* 継承先から継承元の隠蔽されたメンバを呼び出すには。。。
=== 隠蔽されたメンバを呼び出してみる ===
{{url>https://replit.com/@youetsusato/inpeidagemu?v=1&embed=true#main.cpp 600,600}}
この関係は、オーバーロードでも、オーバーライドでもないことに気を付けてください。\\
(オーバーライドはまだ習ってないけどね!)\\
継承関係のクラスの間で同じメンバ名があったときは、外側のクラスのインスタンスが優先される
これを派生クラスのメンバが、基本クラスのメンバを隠ぺいしている。と言う。
この時に、cCylinder::PrintMember()は何も考えなくてもcCylinderクラスのインスタンスから呼び出せることがわかりました。\\
それでは、cCylinderのインスタンス経由で、cBase::PrintMember()を呼び出すことはできないのでしょうか?\\
ただ、同じPrimtMemberという名前がかぶって、覆い隠されているだけなら、名前を呼び分けることができれば呼び出せるのではないでしょうか?\\
呼び出してみます。\\
cBase::PrintMember()はpublicアクセスなので、cCylinderのインスタンスから、\\
cCylinder ins;
ins.cBase::PrintMemeber();
のように、直接クラス指定をすることで呼び出せます。\\
また、クラスの関数定義部でも、\\
void cBase::PrintMember()//メンバ関数(公開)
{
cBase::PrintMember();
//this->cBase::PrintMember();
cout << "中心: (" << x << "," << y << ")" << endl;
cout << "半径:" << r << endl;
cout << "面積:" << area << endl;
}
のように呼び出せる。\\
(ので、cBaseのプライベートメンバーの値もめでたく表示できるね!(つぎたしだから順番変だけどw))\\