Coursera課程筆記----C++程序設計----Week5

繼承與派生(Week 5)

繼承&派生

基礎概念

  • 繼承:在定義一個新的類B時,若是該類與某個已有的類A類似(B擁有A的所有特色),那麼就能夠把A做爲一個基類,而把B做爲基類的一個派生類(子類)ios

  • 派生類是經過對基類進行修改和擴充獲得的,在派生類中,能夠擴充新的成員變量和成員函數數組

  • 派生類一經定義後,能夠獨立使用,不依賴於基類函數

  • 派生類擁有基類的所有成員函數和成員變量,不管是private、protected、public.學習

    • 在派生類的各個成員函數中,不能訪問基類中的private成員

派生類的寫法

class 派生類名: public 基類名
{
  
};
class CStudent{
  private:
  string sName;
  int nAge;
  public:
  bool IsThreeGood(){};
  void SrtName(const string & name)
  {sName = name;}
  //......
};
class CUndergraduateStudent: public CStudent{
  private:
  int nDepartment;
  public:
  bool IsThreeGood(){...};//覆蓋
  bool CanBaoYan(){...};
};
class CGraduatedStudent:public CStudent{
  private:
  int nDepartment;
  char szMentorName[20];
  public:
  int CountSalary(){...};
};

派生類對象的內存空間

派生類對象的體積,等於基類對象的體積+派生類對象本身的成員變量的體積。在派生類對象中,包含着基類對象。並且基類對象的存儲位置位於派生類對象新增的成員變量以前ui

複合關係和繼承關係

類之間的兩種關係

  • 繼承:「是」關係
    • 基類A,B是基類A的派生類
    • 邏輯上要求:「一個B對象也一個A對象」
  • 複合:「有」關係
    • 類C中「有」成員變量k,k是類D的對象,則C和D是複合關係
    • 通常邏輯上要求:「D對象是C對象的固有屬性或組成部分

繼承關係的使用

  • 寫了一個CMan類表明男人
  • 後來又發現須要一個CWoman類來表明女人
  • 好的作法是歸納男人和女人的共同特色,寫一個CHuman類表明人,CMan和CWoman都今後派生。
  • image-20200519135022156

複合關係的使用

  • 幾何形體程序中,須要寫「點」類,也須要寫「圓」類,二者的關係就是複合關係——每個圓對象裏都一個點對象(圓心)
class CPoint
{
  double x,y;
  friend class CCircle;//便於Ccircle類操做其圓心
};
class CCircle
{
  double r;
  CPoint center;
};
  • 避免循環定義
  • image-20200519140948166

基類/派生類同名成員與Protected關鍵字

基類和派生類有同名成員的狀況

class base{
  int j;
  public:
  int i;
  void func();
}

class derived: public base{
  public:
  int i;
  void access();
  void func();
}

void derived::access()
{
  j = 5;//error,j是基類的私有成員變量
  i = 5;//引用的是派生類的i
  base::i = 5;//引用的是基類的i
  func(); //派生類的
  base::func();//基類的
}

derived obj;
obj.i = 1;//派生類賦值
obj.base::i = 1;//基類賦值
  • 通常來講,基類和派生類不定義同名成員變量

訪問範圍說明符

  • 基類的private成員:能夠被下列函數訪問this

    • 基類的成員函數
    • 基類的友元函數
  • 基類的public成員:能夠被下列函數訪問spa

    • 基類的成員函數
    • 基類的友元函數
    • 派生類的成員函數
    • 派生類的友元函數
    • 其餘的函數
  • 基類的protected成員:能夠被下列函數訪問3d

    • 基類的成員函數
    • 基類的友元函數
    • 派生類的成員函數能夠訪問當前對象的基類的保護成員
class Father{
  private: int nPrivate;
  public: int nPublic;
  protected: int nProtected;
};
class Son: public Father{
  void AccessFather(){
    nPublic = 1; //ok
    nPrivate = 1;//wrong
    nProtected = 1;//OK,訪問當前對象從基類繼承的protected成員
    Son f;
    f.nProtected = 1;//wrong,f不是當前對象
  }
};

派生類的構造函數

基本概念

  • 派生類對象包含基類對象指針

  • 執行派生類構造函數以前,先執行基類的構造函數code

  • 派生類交代基類初始化,具體形式:

    構造函數名(形參表):基類名(基類構造函數實參表)

舉例

class Bug{
  private:
  int nLegs; int nColor;
  public:
  int nType; 
  Bug(int legs, int color);
  void PrintBug(){ };
};
class FlyBug:public Bug{
  int nWings;
  public:
  FlyBug(int legs, int color, int wings);
};

Bug::Bug(int legs, int color){
  nLegs = legs;
  nColor = color;
}
FlayBug::FlyBug(int legs, int color, int wings):Bug(legs,color){
  nWings = wings;
}
  • 在建立派生類的對象時

    • 須要調用基類的構造函數:初始化派生類對象中從基類繼承的成員
    • 在執行一個派生類的構造函數以前,老是先執行基類的構造函數
  • 調用基類構造函數的兩種方式

    • 顯式方式,如上例所示
    • 隱式方式:派生類的構造函數中,省略基類構造函數時,派生類的構造函數自動調用基類的默認構造函數
  • 派生類的析構函數被執行時,執行完派生類的析構函數後,自動調用基類的析構函數

包含成員對象的派生類的構造函數

class Skill{
  public:
  skill(int n){ }
};

class FlyBug: public Bug{
  int nWings;
  Skill sk1,sk2;
  public:
  FlyBug(int legs,int color,int wings);
};
FlyBug::FlyBug(int legs, int color, int wings):Bug(legs,color),sk1(5),sk2(color){
  nWings = wings;
}
  • 建立派生類的對象時,執行派生類的構造函數以前
    • 調用基類的構造函數,初始化派生類對象中從基類繼承的成員
    • 調用成員對象類的構造函數,初始化派生類對象中的成員對象
  • 執行完派生類的析構函數後
    • 調用成員對象類的析構函數
    • 調用基類的析構函數
  • 析構函數的調用順序與構造函數的調用順序相反

public繼承的賦值兼容規則

基本概念

class base{};
class derived: public base{ };
base b;
derived d;
  1. 派生類的對象能夠賦值給基類對象 b = d;
  2. 派生類對象能夠初始化基類引用 base & br = d;
  3. 派生類對象的地址能夠賦值給基類指針 base *pb = & d;

直接基類與間接基類

  • 類A派生B,B派生C,C派生D
    • A是B的直接基類
    • B是C的直接基類,A是C的簡介基類
  • 在聲明派生類時,只須要列出它的直接基類
    • 派生類沿着類的層次自動向上繼承它的間接基類
    • 派生類的成員包括
      • 派生類本身定義的成員
      • 直接基類中的全部成員
      • 全部間接基類的所有成員

練習

Quiz 1

注:填空題在Coursera提交時,文件中只需出現填進去的內容便可

#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
class MyString: public string {
public:
    MyString(string string1):string(string1){};
    MyString():string(){};
    MyString(const char* a):string(a){};
    string operator()(int a,int b){
        return substr(a,b);
    }
};

int CompareString( const void * e1, const void * e2) {
    MyString * s1 = (MyString * ) e1;
    MyString * s2 = (MyString * ) e2;
    if( *s1 < *s2 ) return -1;
    else if( *s1 == *s2 ) return 0;
    else if( *s1 > *s2 ) return 1;
}
int main() {
    MyString s1("abcd-"),s2,s3("efgh-"),s4(s1);
    MyString SArray[4] = {"big","me","about","take"};
    cout << "1. " << s1 << s2 << s3<< s4<< endl;
    s4 = s3; s3 = s1 + s3;
    cout << "2. " << s1 << endl;
    cout << "3. " << s2 << endl;
    cout << "4. " << s3 << endl;
    cout << "5. " << s4 << endl;
    cout << "6. " << s1[2] << endl;
    s2 = s1; s1 = "ijkl-";
    s1[2] = 'A' ;
    cout << "7. " << s2 << endl;
    cout << "8. " << s1 << endl;
    s1 += "mnop";
    cout << "9. " << s1 << endl;
    s4 = "qrst-" + s2;
    cout << "10. " << s4 << endl;
    s1 = s2 + s4 + " uvw " + "xyz";
    cout << "11. " << s1 << endl;
    qsort(SArray,4,sizeof(MyString), CompareString);
    for( int i = 0;i < 4;++i )
        cout << SArray[i] << endl;
    //輸出s1從下標0開始長度爲4的子串
    cout << s1(0,4) << endl;
    //輸出s1從下標爲5開始長度爲10的子串
    cout << s1(5,10) << endl;
    return 0;
}

Quiz2 魔獸世界之二:裝備

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
const int WARRIOR_NUM = 5;
const int WEAPONS_NUM = 3;
/*
string Warrior::names[WARRIOR_NUM] = { "dragon","ninja","iceman","lion","wolf" };
紅方司令部按照 iceman、lion、wolf、ninja、dragon 的順序製造武士。
藍方司令部按照 lion、dragon、ninja、iceman、wolf 的順序製造武士。
*/

class Headquarter;
class Warrior
{
private:
    Headquarter * pHeadquarter; //指向英雄所屬陣營的指針
    int kindNo;//武士的種類編號 0 dragon 1 ninja 2 iceman 3 lion 4 wolf
    int no;//英雄編號
public:
    static string weapons[WEAPONS_NUM];//存放3種武器名字的數組
    static string names[WARRIOR_NUM]; //存放5種職業名字的數組
    static int initialLifeValue [WARRIOR_NUM]; //存放不一樣英雄的起始生命值(從輸入中採集)
    Warrior( Headquarter *p,int no_,int kindNo_);//構造函數
    void PrintResult(int nTime); //執行打印數據的工做,若沒法繼續建立則輸出結束並中止
};

class dragon: public Warrior
{
private:
    int weaponNum1;//dragon有1件武器,武器編號 0 sword 1 bomb 2 arrow
    double morale;//dragon的士氣值
public:
    dragon(Headquarter *p,int no_,int kindNo_);
    void PrintResult(int nTime);
};

class ninja: public Warrior
{
private:
    int weaponNum1,weaponNum2;//ninja有2件武器,武器編號 0 sword 1 bomb 2 arrow
public:
    ninja(Headquarter *p,int no_,int kindNo_);
    void PrintResult(int nTime);
};

class iceman: public Warrior
{
private:
    int weaponNum1;//iceman有1件武器,武器編號 0 sword 1 bomb 2 arrow
public:
    iceman(Headquarter *p,int no_,int kindNo_);
    void PrintResult(int nTime);
};

class lion: public Warrior
{
private:
    int loyalty;//lion的忠誠度
public:
    lion(Headquarter *p,int no_,int kindNo_);
    void PrintResult(int nTime);
};
//wolf由於沒有特色,故不須要專門的類

class Headquarter
{
private:
    int totalLifeValue;
    bool stopped;
    int totalWarriorNum;
    int color;
    int curMakingSeqIdx; //當前要製造的武士是製造序列中的第幾個
    int warriorNum[WARRIOR_NUM]; //存放每種武士的數量
    Warrior * pWarriors[1000];//和每一個建立的英雄創建連接
public:
    friend class Warrior;
    friend class ninja;
    friend class lion;
    friend class dragon;
    friend class iceman;
    static int makingSeq[2][WARRIOR_NUM];//武士的製做序列,按陣營的不一樣分紅兩個
    void Init(int color_, int lv); //初始化陣營須要顏色和總血量
    ~Headquarter();
    int Produce(int nTime); //建立英雄,輸入時間
    string GetColor();
};

Warrior::Warrior(Headquarter *p, int no_, int kindNo_) {
    no = no_;
    kindNo = kindNo_;
    pHeadquarter = p;
}

void Warrior::PrintResult(int nTime) {
    string color = pHeadquarter->GetColor();
    printf("%03d %s %s %d born with strength %d,%d %s in %s headquarter\n",
           nTime, color.c_str(), names[kindNo].c_str(),no,initialLifeValue[kindNo],
           pHeadquarter->warriorNum[kindNo],names[kindNo].c_str(),color.c_str()); // string 在printf中輸出的函數調用c_str()
}

dragon::dragon(Headquarter *p, int no_, int kindNo_):Warrior(p,no_,kindNo_) {
    weaponNum1 = no_ % 3;
    morale = (double)p->totalLifeValue / (double)initialLifeValue[kindNo_];
}

void dragon::PrintResult(int nTime) {
    Warrior::PrintResult(nTime);
    printf("It has a %s,and it's morale is %.2f\n",
            weapons[weaponNum1].c_str(),morale);
}

ninja::ninja(Headquarter *p, int no_, int kindNo_) :Warrior(p,no_,kindNo_){
    weaponNum1 = no_ % 3;
    weaponNum2 = (no_+1) % 3;
}

void ninja::PrintResult(int nTime) {
    Warrior::PrintResult(nTime);
    printf("It has a %s and a %s\n",
           weapons[weaponNum1].c_str(),weapons[weaponNum2].c_str());
}

iceman::iceman(Headquarter *p, int no_, int kindNo_) :Warrior(p,no_,kindNo_){
    weaponNum1 = no_ % 3;
}

void iceman::PrintResult(int nTime) {
    Warrior::PrintResult(nTime);
    printf("It has a %s\n",
           weapons[weaponNum1].c_str());
}

lion::lion(Headquarter *p, int no_, int kindNo_) :Warrior(p,no_,kindNo_){
    loyalty = p->totalLifeValue;
}

void lion::PrintResult(int nTime) {
    Warrior::PrintResult(nTime);
    printf("It's loyalty is %d\n",
            loyalty);
}



void Headquarter::Init(int color_, int lv) {
    color = color_;
    totalLifeValue = lv;
    totalWarriorNum = 0;
    stopped = false;
    curMakingSeqIdx = 0;
    for (int i = 0; i < WARRIOR_NUM; i++) {
        warriorNum[i] = 0;
    }
}

Headquarter::~Headquarter() {
    for (int i = 0; i < totalWarriorNum; i++) {
        delete pWarriors[i];
    }
}

int Headquarter::Produce(int nTime) {
    if(stopped)
        return 0;
    int searchingTimes = 0;
    while(Warrior::initialLifeValue[makingSeq[color][curMakingSeqIdx]] > totalLifeValue &&
          searchingTimes < WARRIOR_NUM)
    {
        curMakingSeqIdx = (curMakingSeqIdx + 1) % WARRIOR_NUM;
        searchingTimes++;
    }
    int kindNo = makingSeq[color][curMakingSeqIdx];
    if(Warrior::initialLifeValue[kindNo] > totalLifeValue)
    {
        stopped = true;
        if(color == 0)
            printf("%03d red headquarter stops making warriors\n",nTime);
        else
            printf("%03d blue headquarter stops making warriors\n",nTime);
        return 0;
    }
    //排除全部其餘條件後,開始製做士兵
    totalLifeValue -= Warrior::initialLifeValue[kindNo];
    curMakingSeqIdx =( curMakingSeqIdx + 1) % WARRIOR_NUM;
    if(kindNo == 0) {
        pWarriors[totalWarriorNum] = new dragon(this, totalWarriorNum + 1, kindNo);
        warriorNum[kindNo]++;
        dragon* p = (dragon *)pWarriors[totalWarriorNum];
        p->PrintResult(nTime);
        totalWarriorNum++;
        return 1;
    }
    else if(kindNo == 1){
        pWarriors[totalWarriorNum] = new ninja(this,totalWarriorNum+1,kindNo);
        warriorNum[kindNo]++;
        ninja* p = (ninja *)pWarriors[totalWarriorNum];
        p->PrintResult(nTime);
        totalWarriorNum++;
        return 1;
    }
    else if(kindNo == 2){
        pWarriors[totalWarriorNum] = new iceman(this,totalWarriorNum+1,kindNo);
        warriorNum[kindNo]++;
        iceman* p = (iceman *)pWarriors[totalWarriorNum];
        p->PrintResult(nTime);
        totalWarriorNum++;
        return 1;
    }
    else if(kindNo == 3){
        pWarriors[totalWarriorNum] = new lion(this,totalWarriorNum+1,kindNo);
        warriorNum[kindNo]++;
        lion* p = (lion *)pWarriors[totalWarriorNum];
        p->PrintResult(nTime);
        totalWarriorNum++;
        return 1;
    }
    else if(kindNo == 4){
        pWarriors[totalWarriorNum] = new Warrior(this,totalWarriorNum+1,kindNo);
        warriorNum[kindNo]++;
        pWarriors[totalWarriorNum]->PrintResult(nTime);
        totalWarriorNum++;
        return 1;
    }
}

string Headquarter::GetColor() {
    if(color == 0)
        return "red";
    else
        return "blue";
}

string Warrior::names[WARRIOR_NUM] = {"dragon","ninja","iceman","lion","wolf"};
string Warrior::weapons[WEAPONS_NUM] = {"sword","bomb","arrow"};
int Warrior::initialLifeValue[WARRIOR_NUM];
int Headquarter::makingSeq[2][WARRIOR_NUM]={{2,3,4,1,0},{3,0,1,2,4}};//兩個司令部武士的製做順序序列

int main()
{
    int t;
    int m;
    Headquarter RedHead,BlueHead;
    scanf("%d", &t); //讀取case數
    int nCaseNo = 1;
    while(t--){
        printf("Case:%d\n",nCaseNo++);
        scanf("%d",&m);//讀取基地總血量
        for (int i = 0; i < WARRIOR_NUM; i++) {
            scanf("%d",&Warrior::initialLifeValue[i]);
        }
        RedHead.Init(0,m);
        BlueHead.Init(1,m);
        int nTime = 0;
        while (true){
            int tmp1 = RedHead.Produce(nTime);
            int tmp2 = BlueHead.Produce(nTime);
            if( tmp1 == 0 && tmp2 == 0)
                break;
            nTime++;
        }
    }
    return 0;
}
/*
 * 魔獸世界2就是在魔獸世界1的基礎上作一些改動
 * 雖然輸出結果是正確的沒錯……可我總以爲Produce的函數被我寫的有點囉嗦Orz
 * 父類指針指向子類對象想調用子類函數還真是有點麻煩呢……
 * 或許會有更好的方法?在之後的學習中試試看吧
 * 搜索的時候發現了virtual之類的東西……後面應該會學到?
 */
相關文章
相關標籤/搜索