===== 引き続き継承の話 =====
既存のクラスを基にして新しいクラスを作ることを,クラスの継承 (**inheritance**) と呼ぶ。\\
継承の話をするために、(良い設計ではないが)円を表すクラスと、円柱を表すクラスを考える。\\
円を表すクラス
; 名前
: cBase
: 継承の話をしたいのでついベースクラスって意味で命名してしまったが\\ ふつうは、当然作るオブジェクトに即した名前を付ける\\
; メンバ変数
: 変数名 x, y => 中心座標(整数) 非公開
: 変数名 r => 半径(実数) 公開
: 変数名 area => 面積(実数) 非公開
; メンバ関数
: cBase() => デフォルトコンストラクタ(デフォルトコンストラクタは引数なしコンストラクタのこと)
: cBase(int _x, int _y, double _r) => (引数付きコンストラクタ)
: void PrintMember() => 自身のメンバを表示する関数
: void SetArea() => 半径をもとに免責を計算してareaにセットする関数
: double GetArea() => areaを返す関数
{{:game-engineer:classes:2022:game-programing-1:second-term:12:class_fig.png?600|}}
cBaseクラスの詳細
=== 実際のソースコード ===
//実はインデントは2日4つの半角スペースが標準派がおおい。
//今後参考にしていくgoogleのコード規約でもスペース
class cBase
{
int x, y; //メンバ変数
public: //↓ここからのメンバはパブリック(公開)
cBase() //コンストラクタは2つオーバーロードしてある
:x(0), y(0), r(0.0), area(0) {
cout << "生誕" << endl;
} //メンバイニシャライザによる初期化
cBase(int _x, int _y, double _r)
:x(_x), y(_y), r(_r),area(0) {//areaは0を入れておいて後で計算
cout << "生誕" << endl;
SetArea();//areaを計算
}
~cBase() { cout << "消滅" << endl; } //デストラクタ
double r;//メンバ変数
void PrintMember(); //メンバ関数(公開)宣言のみ
double GetArea() { return(area); }
private://↓ここからのメンバは再びプライベート(非公開)
double area;//メンバ変数
void SetArea(); //面積を計算してareaにセットする関数 宣言のみ
};
#include "cBase.h"
void cBase::PrintMember()//メンバ関数(公開)
{
cout << "中心: (" << x << "," << y << ")" << endl;
cout << "半径:" << r << endl;
cout << "面積:" << area << endl;
}
void cBase::SetArea() //面積を計算してareaにセットする
{
const double pi = 3.14159265359;
area = pi * r * r;
}
// cBase:: の中ではpublic,private区別
=== 使っていくー ===
#include
#include "cBase.h"
#include "cCylinder.h"
using std::cout;
using std::cin;
using std::endl;
int main()
{ //特にコンストラクタを指定しないと引数なしコンストラクタが呼ばれる
cBase inVar; //クラスからできた変数=インスタンス=C++でのオブジェクト
cBase inVar2(10, 5, 3.0);
inVar.PrintMember();
inVar2.PrintMember();
return(0);
}
* 実行結果
{{:game-engineer:classes:2022:game-programing-1:second-term:12:実行結果1.png?200|}}
実行結果
==== cBaseクラスまとめ ====
クラスを設計するには、そのクラスをモデル化(抽象化)することが大事です。\\
簡単には、\\
そのオブジェクトを表すのに必要なパラメータ => メンバ変数\\
そのオブジェクトのふるまいを表すもの => メンバ関数\\
になります。\\
__**cBaseクラスの場合**
__
* cBaseクラスは、そのメンバ変数により円図形をあらわす
* メンバ変数、中心(x, y)、半径\(r\)、面積(area)で円の状態を表す
* PrintMember()関数で、その円のパラメータ一覧を表示する
* 半径が設定してあると、SetArea()関数で面積を計算できる
* 面積(area)はprivate属性なので、インスタンスからは直接参照できない
* GetArea()関数により、areaの値をゲットできる
===== 継承してみる =====
次は、先ほどのcBaseクラスを継承して、cCylinderクラスを作ってみます。\\
説明のために無理やり作ったクラスなので設計としてはよくありません。(むしろダメ)\\
余裕のある人はこの設計のどこがダメなのか考えてみよう!\\
__hint: 前に言った、基底クラスと派生クラスの関係は、どんなかんけいになってるのがよかったっけ?__\\
作るクラスは以下のようなクラスにします。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:class_inheri.png?600|}}
円柱は、底面と上面に円があり、側面を壁にした形状です。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:シリンダ.png?400|}}
考え方としては、形状(円)を拡張して(高さと体積の属性をプラス)して円柱クラスを作る。\\
です。
; 名前
: cCylinder
; 基底クラス
: cBase
; メンバ変数
: 変数名 height 高さ(実数) 非公開
: 変数名 volume 体積(実数) 非公開
; メンバ関数
: cCylinder() => デフォルトコンストラクタ
: cCylinder(int _x, int _y, double _r, double height) => (引数付きコンストラクタ)
: void SetVolume() => 半径と高さをもとに体積を計算してvolumeにセットする関数
=== コードを書いていくぅ ===
#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;
}
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?300|}}
PrintMember関数が、cBaseクラスに実装されているので、新しく追加したメンバを表示できていませんね(上面と底面の円の属性を表示しちゃってる)。\\
次はこれをどうしたらcCylinderクラスのメンバ詳細を表示できるのかってのを考えてみます。