C++講座
もくじ
第3章『継承』
第三の壁�ポリモーフィズム
継承
class Eigorian{
public:
int hp;
}
class Pinkri
: public Eigorian{
public:
int mp;
}
class Koshiteru
: public Eigorian{
public:
int sp;
}
えいごリアンクラスを継承した�ぴんくりクラスと�こしてるクラスを作る
: public (クラス名)
を付けると、そのクラスを
継承するという意味になる�(publicの意味は割愛)
継承元になるクラスを
「親クラス」という
継承してできたクラスを
「子クラス」という
継承
継承すると、
親クラスのデータを
そのまま受け継ぐ
ことができる
class Eigorian{
public:
int hp;
}
class Pinkri : public Eigorian{
public:
int mp;
}
class Koshiteru : public Eigorian{
public:
int sp;
}
えいごリアン:HPだけ
ぴんくり:HPとMP
こしてる:HPとSP
関数のオーバーライド
親の関数を子が持つと、子の関数が優先される
class Eigorian{
public:
int hp;
void Magic();
}
class Pinkri : public Eigorian{
public:
int mp;
void Magic();
}
Pinkri型の変数でMagicを呼ぶと
EigorianではなくPinkriのMagicが
呼ばれる(上書きされる)
関数のオーバーライド
関数のオーバーライドの実装
void Eigorian::Magic(){
cout << “えいごリアンの魔法” << endl;
}
void Pinkri::Magic(){
cout << “ぴんくりの魔法” << endl;
}
Pinkri::Magicで
Eigorian::Magicも
呼ぶようにするには
どうしたらいいか?
親の処理の後に
子の処理を続ける
ようにする
関数のオーバーライド
関数のオーバーライドの実装
void Eigorian::Magic(){
cout << “えいごリアンの魔法” << endl;
}
void Pinkri::Magic(){
Eigorian::Magic();
cout << “ぴんくりの魔法” << endl;
}
Eigorian::Magicを
呼べばいい
コンストラクタのオーバーライド
子クラスを作るとき、コンストラクタで
親クラスのコンストラクタを呼ぶことができる
class Eigorian{
public:
int hp;
Eigorian(int h);
}
class Pinkri : public Eigorian{
public:
int mp;
Pinkri(int h, int m);
}
コンストラクタのオーバーライド
コンストラクタの実装部分で
呼びたい親クラスのコンストラクタを記述する
void Eigorian::Eigorian(int h){
hp = h;
}
void Pinkri::Pinkri(int h, int m) : Eigorian(h){
mp = m;
}
Eigorianのコンストラクタ
でHPだけ設定して
もらいたいので、
HPの値(h)だけを
与えてコンストラクタを
実行する
デストラクタのオーバーライド
親クラスのデストラクタには必ずvirtualを付ける
消去時に自動で親クラスのデストラクタが呼ばれる
(コンストラクタのような親クラスの指定は不要)
class Eigorian{
public:
int hp;
Eigorian(int h);
virtual ~Eigorian();
}
class Pinkri : public Eigorian{
public:
int mp;
Pinkri(int h, int m);
~Pinkri();
}
コンストラクタとデストラクタ
コンストラクタの処理順
親クラス
子クラス
孫クラス
デストラクタの処理順
孫クラス
子クラス
親クラス
仮想関数
親クラスや子クラスのオブジェクトはそれぞれ
継承関係にあっても特に考えなくても使える
int main(){
Eigorian eigo(100); // HP100のえいごリアン生成
Pinkri pin(100, 50); // HP100、MP50のぴんくり生成
}
int main(){
Eigorian *eigo = new Eigorian(100);
Pinkri *pin = new Pinkri(100, 50);
delete eigo;
delete pin;
}
仮想関数
親クラスのポインタ型で
子クラスのオブジェクトを作ると?
int main(){
Eigorian *p = new Pinkri(100, 50);
p->Magic();
delete p;
}
このMagicは
EigorianのMagicが
呼ばれてしまう
仮想関数
親クラスの関数にvirtualを付けると、その関数は
仮想関数になる(継承先で処理が変わる関数)
class Eigorian{
public:
int hp;
virtual void Magic();
}
class Pinkri : public Eigorian{
public:
int mp;
void Magic();
}
virtual を関数宣言の
頭に付ける
(実装側には付けない)
仮想関数
EigorianのMagicがvirtualだと、
この場合はちゃんとPinkriのMagicが呼ばれる
int main(){
Eigorian *p = new Pinkri(100, 50);
p->Magic();
delete p;
}
これはPinkriのMagic
オーバーライドする元の
関数は常に頭にvirtualを
付けるのがよい
抽象クラス
仮想関数の中身をあえて設定せず、
継承した子クラスの方で実装してもらうようにするには
更に関数宣言の後ろに=0を付ける(純粋仮想関数)
関数の中身は空っぽだという意味である
class Eigorian{
public:
int hp;
virtual void Magic() = 0; // Eigorian::Magicは実装不要
}
純粋仮想関数が一つでもあるクラスは
「抽象クラス」といって、オブジェクトを
作れなくなる(継承前提の親クラスとなる)
抽象クラス
int main(){
Eigorian eigo; // エラーになる(Eigorianは抽象クラス)
Eigorian *p = new Eigorian(100); // これもエラー
}
int main(){
Eigorian *p = new Pinkri(100, 50); // これはOK�(作ってるオブジェクトはEigorianじゃなくてPinkriだから)
delete p;
}
ポリモーフィズム
抽象クラスはそれ単体では役に立たないが、
継承した子クラスによって行いたい処理を変えたい
場合はとても便利である
ポリモーフィズムという
Charaクラス
(抽象クラス)
Speak()が純粋仮想関数
それぞれ継承
Pinkriクラス
Baysukeクラス
Knyackiクラス
Pinkri::Speak()
「ぴんくりに気をつけてください」
Baysuke::Speak()
「オイラベイ助だよ!!」
Knyacki::Speak()
「ニャッ嫌いなのか?」
ポリモーフィズム
各クラスの宣言
class Chara{
public:
virtual void Speak() = 0; // 話す純粋関数
};
class Pinkri : public Chara{
public:
void Speak(); // ぴんくり会話
};
class Baysuke : public Chara{
public:
void Speak(); // ベイ助会話
};
class Knyacki : public Chara{
public:
void Speak(); // ニャッキ会話
};
ポリモーフィズム
各クラスの定義
void Pinkri::Speak(){
cout << “ぴんくりに気をつけてください” << endl;
}
void Baysuke::Speak(){
cout << “オイラベイ助だよ!!” << endl;
}
void Knaycki::Speak(){
cout << “ニャッ嫌いなのか?” << endl;
}
ポリモーフィズム
各オブジェクトを生成してしゃべらせる
int main(){
Chara *pin = new Pinkri;
Chara *bay = new Baysuke;
Chara *knya = new Knaycki;
pin->Speak();
bay->Speak();
knya->Speak();
delete pin;
delete bay;
delete knya;
}
Pinkri::Speak()
「ぴんくりに気をつけてください」
Baysuke::Speak()
「オイラベイ助だよ!!」
Knyacki::Speak()
「ニャッ嫌いなのか?」
ポリモーフィズム
配列を用いた同じ処理の別表現
int main(){
Chara *chara[3];
chara[0] = new Pinkri;
chara[1] = new Baysuke;
chara[2] = new Knyacki;
for (int i = 0; i < 3; i++){
chara[i]->Speak();
}
for (int i = 0; i < 3; i++){
delete chara[i];
}
}
Speak()の部分はこれ一行
だが、オブジェクトに応じて
しゃべる内容を自動で
変えてくれる