7.类和对象_多态

//多态的分类:静态多态和动态多态
//静态多态:函数重载和运算符重载属于静态多态,复用函数名
//动态多态:派生类和虚函数实现运行时多态
//区别:
// 静态:函数地址早绑定,编译阶段确定
// 动态:函数地址晚绑定,运行阶段确定
//动态多态条件:
// 1.有继承关系
// 2.重写父类的虚函数(不是重载,函数返回值类型,函数名,参数列表完全相同)
//对于子类重写时,子类的virtual关键字可写可不写
//动态多态的使用:
// 父类的指针或引用指向子类的对象
//纯虚函数和抽象类
//通常父类虚函数毫无意义,主要调用子类重写的内容
//因此可以将虚函数改写成纯虚函数
//语法:virtual 返回值类型 函数名 (参数列表) = 0;
//当类中有纯虚函数,这个类叫抽象类
//抽象类特点:1.无法实例化对象 2.子类必须重写抽象类的纯虚函数,否则也属于抽象类
//虚析构和纯虚析构
//共性:可以解决父类指针释放子类对象;都要有具体的函数实现
//区别:纯虚析构属于抽象类,无法实例化对象
//父类指针在析构时,不会调用子类的析构函数,导致子类如果有堆区属性,会内存泄漏
//利用虚析构可以解决父类指针释放子类对象时不干净的问题
//虚析构和纯虚析构都需要代码实现:
// 纯虚析构,在类的外部:类名::~类名(){代码实现}
//有纯虚析构后,这个类也属于抽象类,无法实例化对象
//使用条件:子类的析构是必需的
#include <iostream>
#include <string>
using namespace std;

class Animal
{
public:
    //virtual void speak() 
    //{
    //  cout << "Animal speaking" << endl;
    //}
    virtual void speak() = 0;

};
//Animal类内部
//vfptr - 虚函数(表)指针,指向vftable(表内部记录虚函数地址)
//Cat类内部
//vfptr指向vftable
//当子类重写父类的虚函数
//子类中的虚函数表内部会替换成子类的虚函数地址


class Cat :public Animal 
{
public:
    void speak()
    {
        cout << "Meow~" << endl;
    }
};

class Dog :public Animal
{
public:
    void speak()
    {
        cout << "Woof~" << endl;
    }
};

void doSpeak(Animal &animal) //Animal& animal = cat; 相当于父类引用指向子类的对象
{
    //当父类的指针或者引用指向子类对象时
    //发生多态

    //C++允许父子之间类型转换,不需要强制类型转换
    //地址早绑定,在编译阶段就确定了地址
    //如果想猫说话,那么函数地址就不能提前绑定,需要在运行时绑定
    animal.speak();
}

void test01()
{
    Cat cat;
    doSpeak(cat);

    Dog dog;
    doSpeak(dog);
}

//真实开发中,提倡开闭原则
//对扩展开放,对修改关闭

//多态实现计算器
//计算器抽象类
//多态好处:
// -组织结构清晰
// -可读性强
// -对于前期和后期扩展以及维护性高

class CalcAbstract
{
public:
    virtual int getResult() { return 0; }
    int m_Num1;
    int m_Num2;
};

class AddCalc :public CalcAbstract
{
public:
    virtual int getResult()
    {
        return m_Num1 + m_Num2;
    }
};

class SubCalc :public CalcAbstract
{
public:
    virtual int getResult()
    {
        return m_Num1 - m_Num2;
    }
};

class MulCalc :public CalcAbstract
{
public:
    virtual int getResult()
    {
        return m_Num1 * m_Num2;
    }
};

void test02()
{
    //多态使用条件
    //父类的指针或引用指向子类对象
    CalcAbstract* cab = new AddCalc;
    cab->m_Num1 = 10;
    cab->m_Num2 = 10;
    cout << cab->getResult() << endl;
    delete cab;

    cab = new SubCalc;
    cab->m_Num1 = 10;
    cab->m_Num2 = 10;
    cout << cab->getResult() << endl;
    delete cab;

    cab = new MulCalc;
    cab->m_Num1 = 10;
    cab->m_Num2 = 10;
    cout << cab->getResult() << endl;
    delete cab;

}


int main()
{
    //test01();
    //test02();
    system("pause");
    return 0;
}