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之类的东西……后面应该会学到?
 */
原文地址:https://www.cnblogs.com/maimai-d/p/12920608.html