一、继承的含义
- 所谓"继承"就是在一个已存在的类的基础上,再建立一个新类
- 从已有的类派生出新的类,派生类就继承了原有类(基类)的特征,包括成员和方法
- 通过继承可以完成下面的一些功能
3.1 可以在已有类的基础上添加新功能,如对于数组类,可以添加数学计算
3.2可以给类添加数据成员,如对于字符串类,可以派生出一个类,并添加指定成员表示颜色
3.3可以修改类方法的行为,如对于普通英雄类,可以派生出拥有更丰富技能的近战英雄类
二、继承的优点
- 基类定义公共内容,方便统一修改(代码的可重用)
- 重定义基类的成员函数
- 添加新类,新成员方便
注意:
- 派生类对象存储了基类的数据成员,即派生类继承了基类的实现
- 派生类对象可以使用基类的非私有函数,即派生类继承了基类的接口
- 派生类需要自己的构造函数
- 派生类可以根据需要添加额外的数据成员和函数
三、基类和派生类
- 定义子类对象:如果派生类中不实现某个非私有成员方法,会默认调用基类方法;如果派生类实现了基类某个非私有方法,那就会调用子类方法
Warrior warrior; warrior.Move(); //子类实现了就调用子类的,没实现就默认调用基类的实现
- 定义基类指针指向派生类对象 ,那此时才会调用基类的方法,但是不能调用派生类方法
//这里调用基类Move Hero *hero = new Warrior(); //定义基类指针指向派生类对象 hero->Move(); delete hero;
- final关键字,在类名后面修饰,表示该类不能做为基类被其他类继承
class A final { }; class B : public A //此时编译器会报错误 { };
四、有关基类和派生类构造和析构
- 实例化派生类对象时,首先会调用基类的构造函数,再调用派生类的构造函数(析构的顺序和构造的顺序相反,即先调用派生类的析构函数,再调用派生类的析构函数)
- 实例化派生类对象时,如果没有显示的调用基类的构造函数,就会调用基类的默认构造函数
- 派生类构造应通过成员初始化列表将基类信息传递给基类构造
- 应该在派生类构造中初始化派生类新增的数据成员
五、派生类和基类之间的特殊关系小结
- 派生类对象可以使用基类的非私有成员函数和变量
- 基类指针可以在不进行显示类型转换的情况下指向派生类对象(基类指针可以直接指向派生类对象)
- 可以将派生类对象赋值给基类对象,程序将使用隐式重载赋值运算符
注意:
- 基类指针或引用只能调用基类函数,并不能调用派生类函数
- 不可以将基类对象和地址赋值给派生类引用和对象,即不能够做逆操作
- 派生类引用/指针需要强制转换才可以转换成基类对象或引用
六、派生类成员的访问控制(最高访问权限)
- 公有继承:基类的共有成员和受保护成员,在派生类中保持原来的访问属性,其私有成员仍为基类所独有
- 私有继承:基类的公有成员和受保护成员,在派生类中成为了私有成员,私有成员仍为基类所独有
- 受保护继承:基类公有成员和受保护成员,在派生类中成了受保护成员,私有成员仍为基类所独有
//.h文件
class Hero
{
public:
Hero();
Hero(const std::string &nickName, int level, int maxLife, int currLife);
~Hero();
/*基类中的移动方法*/
void Move();
friend ostream & operator << (ostream &out, const Hero &hero);
/*关于const关键字:
1.在方法前:表示返回值是一个const常量 - 只读
2.在方法后:表示方法不可以修改类成员的值
*/
inline const string &GetNickName() const { return m_NickName; }
inline int GetLevel() const { return m_Level; }
inline int GetMaxLife() const { return m_MaxLife; }
inline int GetCurrLife() const {return m_CurrLife;}
inline void SetNickName(const string &nickName) { m_NickName = nickName; }
inline void SetLevel(int level) { m_Level = level; }
inline void SetMaxLife(int maxLife) { m_MaxLife = maxLife; }
inline void SetCurrLife(int currLife) { m_CurrLife = currLife; }
private:
string m_NickName;
int m_Level;
int m_MaxLife;
int m_CurrLife;
};
//cpp 文件
#include "stdafx.h"
#include "Hero.h"
Hero::Hero():Hero("默认英雄",1,100,100) //第一看这种操作(可以调用自己的构造函数)
{
cout << "调用了Hero的默认构造函数" << endl;
}
Hero::Hero(const string &nickName, int level, int maxLife, int currLife):
m_NickName(nickName),m_Level(level),m_MaxLife(maxLife),m_CurrLife(currLife)
{
cout << "调用了Hero的4个参数默认构造函数" << endl;
}
Hero::~Hero()
{
}
void Hero::Move()
{
cout << "普通英雄" << m_NickName << "正在奔跑在大路上" << endl;;
}
ostream & operator << (ostream &out, const Hero &hero)
{
out << "昵称:" << hero.m_NickName << "\n"
<< "等级:" << hero.m_Level << "\n"
<< "最大生命:" << hero.m_MaxLife << "\n"
<< "当前生命:" << hero.m_CurrLife << endl;
return out;
}
//.h文件
#include "Hero.h"
/*战士类*/
class Archmage;
class Warrior : public Hero
{
public:
Warrior();
Warrior(const std::string &nickName,int phyAttack);
~Warrior();
void Move();
void HiSunZi();
void XiaoQuanQuan(Warrior &other);
void XiaoQuanQuan(Archmage &other);
private:
int m_PhysicalAttack; //物理攻击
};
//.cpp 文件
#include "stdafx.h"
#include "Warrior.h"
#include "Archmage.h"
Warrior::Warrior() :Hero("默认战士",1,100,100)
{
cout << "Warrior的构造函数" << endl;
}
Warrior::Warrior(const string &nickName, int phyAttack) : Hero(nickName,1,100,100),m_PhysicalAttack(phyAttack)
{
}
Warrior::~Warrior()
{
}
void Warrior::Move()
{
//派生类中不能直接访问基类的私有成员
cout << "战士 《" << GetNickName() << "》背着一大堆近战武器正在靠近" << endl;
}
void Warrior::HiSunZi()
{
}
void Warrior::XiaoQuanQuan(Warrior &other)
{
cout << GetNickName() << "发出了一剂流星锤" << endl;
cout << other.GetNickName() << "受伤倒地" << endl;
other.SetCurrLife(other.GetCurrLife() - 10);
}
void Warrior::XiaoQuanQuan(Archmage &other)
{
cout << GetNickName() << "为了攻击法师,使了一招野蛮冲撞" << endl;
cout << "法师" << other.GetNickName() << "受到重创" << endl;
other.SetCurrLife(other.GetCurrLife() - 10);
}