====== 今日の授業の雑メモ(後の解説はその下) ======
cEnemy
{
Vec2 moveVec;
public
moveVecはコンストラクタで初期化
}
void cEnemy::Update()
{
//Vec2 moveVec = Vec2(1, 0);
if(画面の右からはみ出たら)
postion_ = position_ + moveVec;
if(画面の左からはみ出たら)
postion_ = position_ + moveVec;
}
//ちょっとわき道にそれた話
Vec2 = Vec2 + Vec2;
postion_ = position_ + moveVec; <-これ
int a,b,c;
a = 5;
b = 3;
c = a + b;
strct point
{
int x,y;
}
point a, b, c;
a.x = 1;
a.y = 0;
b.x = 100;
b.y = 200;
c = a + b;
//本来はこうやらないとだめだよね。
c.x = a.x + b.x;
c.y = a.y + b.y;
moveVec = -1 * moveVec; <-これ
Vec2 = int * Vec2;
ベクトルの掛け算
スカラー×ベクトル
ベクトル×ベクトル
・内積
・外積
val * (x ,y) = (val*x, val*y)
↑なんか、Vec2型を使うと自然とできるよね
演算子のオーバーロード
オーバーロード=引数の型で同じ名前の関数を呼び分けること
⇒ 演算子の前後(左右)の型の組み合わせで、同じ演算子を使ってもその対象に合わせた計算をする
int * Vec2;
double * Vec2;
float * Vec2;
Vec2 + Vec2;
Vec2 - Vec2;
ex)
string a= "my";
string b = " ";
string c = a + b + "friend"; // "my friend" ができる
//string + string に関して +の演算子がオーバーロードされている
オーバーロード =引数の型で同じ名前の関数を呼び分けること
オーバーライド =基底クラスの関数を派生クラスで書き換える(上書き)すること
↑こっちをさっきの時間にやってみたよ
オーバーライドのめんどくさいやつをやります。
====== ポリモーフィズム ======
前回の話の肝:\\
派生クラスのポインタ(アドレス)と基底クラスのポインタには互換性があるよ。\\
基底クラスのポインタ = 派生クラスのポインタ;\\
なんてことができる。\\
===== 仮想関数とアブストラクトクラス =====
{{:game-engineer:classes:2022:game-programing-1:second-term:12:inheri1.png?600|}}
クラスの宣言
クラスを継承するときに、ベースクラスのメンバ関数を、継承先で丸っと上書きしてあげることをオーバライドって言ったね。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:inheri2.png?600|}}
継承の概念
**[ポイント]**\\
オーバーライドされる側の関数(オーバーライドされる可能性がある関数)には何を書くんだっけ?\\
=> メンバ関数の宣言の前に、virtualを書いておく\\
例)\\
virtual void className::memberFunction(int _a, double _b);
オーバーライドした関数(継承側の関数宣言)に花に書くんだっけ?\\
=> メンバ関数の宣言の後ろに overrideを書く\\
例)\\
void className::memberFunction(int _a, double _b) override;\\
==== ちょっと進んだ考え方 ====
ちょいまでに汎化と特化という考え方を学びました。\\
{{:game-engineer:classes:2022:game-programing-1:second-term:12:inheri3.png?600|}}
汎化と特化と共通部分
どちらの場合でもいいですが、クラスの設計をするときに、いろんな継承先で共通になる部分を考えていかなければなりません。\\
汎化の場合が考えやすいですが、共通部分をベースクラスとして作る場合を考えます。\\
例)\\
* 動物クラス -(継承)→ 犬クラス
* 動物クラス -(継承)→ 猫クラス
* 動物クラス -(継承)→ 猿クラス
例えば、ベースとなる動物クラスのメンバ関数に\\
* 起きる関数
* 食べる関数
* 寝る関数
があるとします。それらは、継承先では、\\
* 犬
* 「ムクっと」 起きる
* 「ガブガブ」 食べる
* 「グースカ」 寝る
* 猫
* 「サクっと」 起きる
* 「ニャンニャン」 食べる
* 「ニャースカ」 寝る
* 猿
* 「ムキーと」 起きる
* 「モグモグ」 食べる
* 「すやすや」 寝る
と、それぞれの派生クラスのメンバ関数は別々の動作をすることがわかっています。\\
その場合、ほとんどの場合はベースクラス単体でインスタンスを生成することはない気がします。\\
そんな時は、ベースクラスでは__関数の形の宣言(名前、戻り値、引数)だけ__しておいて、実装は派生クラスに任せるということができます。\\
そのような、派生クラスに、オーバーライドを強制させ実装を任せてしまう仕組みのことを、インターフェースといいます。\\
C++ではインターフェースの機能を実現するためには、純粋仮想関数と抽象クラスという機能を使います。\\
純粋仮想関数は、定義を持たず、宣言時に冒頭にvirtual, 後ろに = 0;を記述したメンバ関数です。\\
例)
virtual void className::memberFunction(int _a, double _b) = 0;
逆に言うと、後ろに =0; をつけると、定義を持つことができません。\\
定義がないということはなんと!。。。インスタンスも作ることができません。\\
(定義なしで、関数の動きが書かれていないんだから仕方ないよね)\\
このように、__純粋仮想関数を1つでも__含み、__インスタンスを生成できないクラス__のことを**抽象クラス(アブストラクトクラス)**と言います。\\
逆に言うと、抽象クラスは、必ず基底クラスとして利用されて、継承先で純粋仮想関数の定義をオーバーライドによって書いてから使います。\\
つまり、抽象クラスの設計者は、実装を派生クラスの定義を書く人に(強制的に書かせる)回すことができます。\\
=== ソースコードのサンプル ===
以下の例では、純粋仮想関数を含むcGameCharクラスを宣言し、それを継承することでcPlayerクラスを作っています。\\
cPlayerクラスでは、cGameCharクラスで形だけ宣言され実装されていないUpdate関数と、Draw関数を実装しています。\\
{{url>https://replit.com/@youetsusato/abusutorakutokurasu?v=1&embed=true#main.cpp 700,600}}