继承与派生(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都从此派生。
复合关系的使用
- 几何形体程序中,需要写“点”类,也需要写“圆”类,两者的关系就是复合关系——每一个圆对象里都有一个点对象(圆心)
class CPoint
{
double x,y;
friend class CCircle;//便于Ccircle类操作其圆心
};
class CCircle
{
double r;
CPoint center;
};
- 避免循环定义
基类/派生类同名成员与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;
- 派生类的对象可以赋值给基类对象 b = d;
- 派生类对象可以初始化基类引用 base & br = d;
- 派生类对象的地址可以赋值给基类指针 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之类的东西……后面应该会学到?
*/