• Coursera课程笔记----C++程序设计----Week5


    继承与派生(Week 5)

    继承&派生

    基础概念

    • 继承:在定义一个新的类B时,如果该类与某个已有的类A相似(B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(子类)

    • 派生类是通过对基类进行修改和扩充得到的,在派生类中,可以扩充新的成员变量和成员函数

    • 派生类一经定义后,可以独立使用,不依赖于基类

    • 派生类拥有基类的全部成员函数和成员变量,不论是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(){...};
    };
    

    派生类对象的内存空间

    派生类对象的体积,等于基类对象的体积+派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象。而且基类对象的存储位置位于派生类对象新增的成员变量之前

    复合关系和继承关系

    类之间的两种关系

    • 继承:“是”关系
      • 基类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成员:可以被下列函数访问

      • 基类的成员函数
      • 基类的友元函数
    • 基类的public成员:可以被下列函数访问

      • 基类的成员函数
      • 基类的友元函数
      • 派生类的成员函数
      • 派生类的友元函数
      • 其他的函数
    • 基类的protected成员:可以被下列函数访问

      • 基类的成员函数
      • 基类的友元函数
      • 派生类的成员函数可以访问当前对象的基类的保护成员
    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不是当前对象
      }
    };
    

    派生类的构造函数

    基本概念

    • 派生类对象包含基类对象

    • 执行派生类构造函数之前,先执行基类的构造函数

    • 派生类交代基类初始化,具体形式:

      构造函数名(形参表):基类名(基类构造函数实参表)

    举例

    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
    ",
               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
    ",
                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
    ",
               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
    ",
               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
    ",
                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
    ",nTime);
            else
                printf("%03d blue headquarter stops making warriors
    ",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
    ",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之类的东西……后面应该会学到?
     */
    
  • 相关阅读:
    traceroute命令
    Apache部署django项目
    Linux中变量#,#,@,0,0,1,2,2,*,$$,$?的含义
    Python正则表达式
    Python 字符串格式化 (%操作符)
    Python初学者的一些编程技巧
    Linux命令 ls -l 输出内容含义详解
    Django 前后台的数据传递示列
    hibernate基础(一)
    MySQL之多表
  • 原文地址:https://www.cnblogs.com/maimai-d/p/12920608.html
Copyright © 2020-2023  润新知