子類別建立
繼承(Inherits)為物件導向程式很重要的機制,可以從既有的類別中擴展新的功能
class A{}; class B:[public|protected|private] A{};
子類別繼承方式有public, protected, private.
private 繼承
private繼承被稱為「實作繼承」, 會將父類別中 public及protected成員變成子類別的private成員, 然後再由子類別提供public方法進行存取.
父類別 | 子類別 |
private | 不繼承 |
protected | private |
public | private |
請參考如下程式碼
#include
using namespace std;
class Pokemon{
public :
int level;
};
class Pikachu:private Pokemon{
public:
Pikachu(){
printf("皮卡丘出生了\n");
}
void setLevel(int l){
this->level=l;
}
int getLevel(){
return level;
}
};
int main(){
Pikachu *p1=new Pikachu();
p1->setLevel(10);
printf("p1 的 level : %d\n", p1->getLevel());
//int t=p1->level;此行會錯誤,
}
protected 繼承
會將父類別中 public及protected成員變成子類別的protected成員
父類別 | 子類別 |
private | 不繼承 |
protected | protected |
public | protected |
public 繼承
父類別 | 子類別 |
private | 不繼承 |
protected | protected |
public | public |
一般都不會使用到private及protected 繼承, 通常都是使用 public 繼承
以public繼承,代表子類別會繼承父類別中宣告為public及protected的成員, private則不繼承
class A{ private: int a=10; public : int b=20; protected : int c=30; }; class B:public A{ //類別B中有隱藏著b跟c二個變數 public: int getA(){ //穩死的,因為B裏面沒有a變數 return a; } int getC(){ return c; } }; int main(){ B *obj=new B(); //printf("%d\n", obj->getA()); printf("b= %d\n", obj->b); printf("c= %d\n", obj->getC()); }
為什麼c要用getC()呢?因為protected 的權限為允許父類別內及子類別內存取,但禁止在物件直接存取
public : 任何地方都可存取
private : 允許自己類別內存取, 子類別內不可存取, 物件不可存取
protected : 允許自己類別內存取, 子類別內亦可存取, 物件不可存取
子類別的擴充
子類別繼承父類別的方法後, 可以再擴充其他功能.
如下代碥 Pikachu *p1=new Pikachu(); 則 p1具有閃電攻擊
但如果以父類別參考子類別物件時, 擴充功能將失效, 如 Pokemon *p2=new Pikachu();
因為p2雖是皮卡丘, 但他是以Pokemon來稱呼, 但不是每個神奇寶貝都具有閃電攻擊的, 所以 lighting當然會失效
但p2還是可以透過強制轉型成 Pikachu, 然後擴充功能就會出現
#include <iostream>
using namespace std;
class Pokemon{
private :
int level;
public:
void setLevel(int l){
this->level=l;
}
int getLevel(){
return level;
}
};
class Pikachu:public Pokemon{
public:
void lighting(){
printf("1萬伏特閃電攻擊\n");
}
};
int main(){
Pikachu *p1=new Pikachu();
p1->lighting();
Pokemon *p2=new Pikachu();
((Pikachu *)p2)->lighting();
}
多型
上述的Pikachu, 直接宣告為 Pikachu就好, 為什麼還要宣告成Pokemon, 然後再來強制轉型, 轉來轉去的呢?
如下代碼說明, pikachuArray 只可以容網Pikachu, 但無法容納Eve,
而 PokemonArray卻可以同時容納Pikachu及Eve
#include <iostream> using namespace std; class Pokemon{ private : int level; public: void setLevel(int l){ this->level=l; } int getLevel(){ return level; } }; class Pikachu:public Pokemon{ public: void lighting(){ printf("1萬伏特閃電攻擊\n"); } }; class Eve:public Pokemon{}; int main(){ Pikachu **pikachuArray=new Pikachu*[10]; pikachuArray[0]=new Pikachu(); pikachuArray[1]=new Eve();//此行無法容納 Eve Pokemon **pokemonArray=new Pokemon*[10]; pokemonArray[0]=new Pikachu(); pokemonArray[1]=new Eve();//此行ok }
方法重載(Overload)
在同一類別裏, 或者在父子類別中, 有著相同的名稱,但不同的參數,叫重載
class A{ public : int num=10; int get(){return num;} }; class B:public A{ public: int num; int get(int x){ num=x; return num; } }; int main(){ B *objb=new B(); //printf("B物件的 num= %d\n", objb->get()); //A的get()雖被B接收了,但又被B的get(int x)多載, //所以objb->get()不能用了 printf("B物件的 num= %d\n", objb->get(100)); printf("B物件的父類別 num= %d\n", objb->A::get()); system("pause"); return 0; }
方法覆載(Override)
子類別的方法名稱與參數跟父類別完全一樣,但子類別還是會保留一份父類別的原始資料
class A{ public : int num=10; int get(){return num;} }; class B:public A{ public: int num=100; int get(){return num;} }; int main(){ B *objb=new B(); printf("B物件的 num= %d\n", objb->get()); printf("B物件的父類別 num= %d\n", objb->A::get()); }
子類別調用父類別方法
子類別覆蓋父類別方法後, 想要調用父類別方法, 就必需使用Pokemon::setLevel(10);
#include <iostream>
using namespace std;
class Pokemon{
private :
int level;
public:
void setLevel(int l){
this->level=l;
}
int getLevel(){
return level;
}
};
class Pikachu:public Pokemon{
public:
Pikachu(){
printf("皮卡丘出生了\n");
}
void setLevel(int l){
printf("從子類別設定等級\n");
Pokemon::setLevel(l);
}
};
int main(){
Pikachu *p1=new Pikachu();
p1->setLevel(10);
printf("p1 的 level : %d\n", p1->getLevel());
}
建構子的重載與繼承
子類別執行建構子時, 會先執行父類別的預設建構子. 因為編譯器會在子類別建構子後面自動加入
Pikachu():Pokemon(){}, 如下藍色字体
#include <iostream>
using namespace std;
class Pokemon{
private :
int level;
public:
Pokemon(){
printf("神奇寶貝出生了\n");
}
};
class Pikachu:public Pokemon{
public:
Pikachu():Pokemon(){
printf("皮卡丘出生了\n");
}
};
int main(){
Pikachu *p1=new Pikachu();
}
父類別具有自訂建構子
當父類別有自訂建構子時, 編譯器不會自動加入預設建構子. 但是子類別會自動調用父類別的預設建構子, 所以子類別就會出錯了.
此時就需在子類別自己寫入要調用的自訂建構子, 如下監色字体
#include <iostream>
using namespace std;
class Pokemon{
private :
int level;
public:
Pokemon(int l){
printf("神奇寶貝出生了\n");
this->level=l;
}
};
class Pikachu:public Pokemon{
public:
Pikachu():Pokemon(1){
printf("皮卡丘出生了\n");
}
};
int main(){
Pikachu *p1=new Pikachu();
}
Singletone 獨生子
當建構子設定為 private時, 就無法在main中new 出物件. 但要防止new 出物件是要用抽象類別, 而不是將建構子設為 private.
將建構子設為 private, 其實是為了只能 new 出一個物件而以, 稱為獨生子, 如下代碼
#include <iostream> using namespace std; class Pokemon{ private : int level; Pokemon(){} public: static Pokemon *obj; static Pokemon *getInstance(){ if(obj==NULL)obj=new Pokemon(); return obj; } }; Pokemon *Pokemon::obj=NULL; int main(){ Pokemon *p1=Pokemon::getInstance(); Pokemon *p2=Pokemon::getInstance(); if(p1==p2)printf("同一隻\n"); else printf("不同隻\n"); }