4.6.7多继承语法
C++中允许一个类继承多个类
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议使用多继承
4.6.8菱形继承--虚继承
概念:两个派生类继承同一个基类,又有某个类同时继承这两个派生类,
这种继承称为菱形继承,或钻石继承;
虚继承:在继承之前加virtual关键字
虚基类:被虚继承的类称为虚基类
虚基类指针 vbptr (virtual base pointer) :虚继承基类的派生类的对象内存中会生成一个vbptr
指向 vbtable 虚基类表 :虚基类表中记录了指针到相同属性数据的偏移量,
通过偏移量找到数据,所以同一属性数据不需要重复存储.
示例:
class Animal
{
public:
int m_Age;
};
//利用虚继承 解决菱形继承问题
//在继承之前加关键字 virtual
//Animal类称为 虚基类
class Sheep:virtual public Animal{};
class Tuo:virtual public Animal{};
class SheepTuo:public Sheep, public Tuo{};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
//当菱形继承,两个父类拥有相同的属性,需要加以作用域区分
cout << "st.Sheep::m_Age=" << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl;
//这份属性数据只需一份,菱形继承导致数据有两份,资源浪费
cout << "st.m_Age=" << st.m_Age << endl;
}
总结: 1.菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
2.利用虚继承可以解决菱形继承问题
4.7多态
4.7.1多态的基本概念
多态是C++面向对象三大特性之一
多态分为两类:
1.静态多态:函数重载和运算符重载属于静态多态,复用函数名;
2.动态多态:派生类和虚函数实现运行时多态;
区别:
1.静态多态的函数地址早绑定,编译阶段确定函数地址;
2.动态多态的函数地址晚绑定,运行阶段确定函数地址;
示例:
class Animal
{
public:
void speak1()
{
cout << "Animal::speak1()" << endl;
}
//虚函数
virtual void speak2()
{
cout << "Animal::speak2()" << endl;
}
};
class Cat
:public Animal
{
public:
void speak1()
{
cout << "Cat::speak1()" << endl;
}
//重写 函数返回值类型 函数名 参数列表 完全相同
void speak2()
{
cout << "Cat::speak2()" << endl;
}
};
void doSpeak(Animal &animal)
{
//地址早绑定 在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址不能提前绑定
//需要在运行阶段进行绑定,地址晚绑定,即实现动态多态
animal.speak1();
cout <<"-----------" << endl;
//函数地址不能提前确定
//需要看传入对象类型
animal.speak2();
//动态多态满足条件
//1.有继承关系
//2.子类要重写父类的虚函数
//动态多态使用
//父类的指针或引用指向子类对象
}
void test01()
{
Cat cat;
doSpeak(cat);//Animal &animal=cat;
}
总结:
动态多态满足条件
1.有继承关系
2.子类要重写父类的虚函数
动态多态使用:父类的指针或引用指向子类对象
重写 函数返回值类型 函数名 参数列表 完全相同
4.7.2多态案例1-计算器类
案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行计算的计算器类
多态优点:
1.代码组织结构清晰
2.可读性强
3.利于前期和后期和扩展以及维护
4.7.3纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数.
纯虚函数语法:
virtual 返回值类型 函数名 (参数列表)=0
当类中有了纯虚函数,这个类称为抽象类.
抽象类特点:
1.无法实例化对象;
2.子类必须重写抽象类中的纯虚函数,否则该子类也是抽象类;
4.7.4多态案例2-制作饮品
案例描述:
制作饮品大致流程为 煮水 冲泡 倒入杯中 加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶
4.7.5虚析构和纯虚析构
多态使用时,如果子类中属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或纯虚析构
虚析构和纯虚析构共性:
1.可以解决父类指针释放子类对象;
2.都需要有具体的函数实现;
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
总结:
1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象;
2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构;
3.拥有纯虚析构函数的类也属于抽象类;