C++核心编程 4 类和对象 - 多态(7)

4.7 多态

4.7.1 多态的基本概念

多态是C++面向对象三大特性之一,多态分为两类:静态多态、动态多态。

静态多态:函数重载和运算符重载属于静态多态,复用函数名

动态多态:派生类和虚函数实现运行时多态

区别:

静态多态的函数地址早绑定 - 编译阶段确定函数地址

动态多态的函数地址晚绑定 - 运行阶段确定函数地址

 

复制代码
#include<iostream>
using namespace std;
//多态

//动物类
class Animal
{
public:
    virtual void speak()
    {
        cout << "动物在说话" << endl;
    }
};

//猫类
class Cat :public Animal
{
public:
    //重写  函数返回值类型  函数名  参数列表  完全相同
    void speak()
    {
        cout << "小猫在说话" << endl;
    }
};

//狗类
class Dog :public Animal
{
public:
    void speak()
    {
        cout << "小狗在说话" << endl;
    }
};

//执行说话的函数
//地址早绑定  在编译阶段确定了函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,既地址晚绑定 改为虚函数即可

//动态多态满足条件:1、有继承关系  2、子类重写父类的虚函数
//动态多态使用 父类的指针或引用 执行子类对象

void doSpeak(Animal &animal)   //Animal &animal = cat  -> animal的引用指向cat  父类的引用指向子类  
{
    animal.speak();
}

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

    Dog dog;
    doSpeak(dog);
}

int main()
{
    test1();
    system("pause");
    return 0;
}
复制代码

总结:多态满足条件

1、有继承关系

2、子类重写父类中的虚函数

多态使用条件

1、父类指针或引用

 

 

 

复制代码
#include<iostream>
#include<string>
using namespace std;
//分别用普通写法和多态技术 实现计算器

//普通写法
class Calculator
{
public:

    int getResult(string oper)
    {
        if (oper == "+")
        {
            return m_Num1 + m_Num2;
        }
        else if(oper == "-")
        {
            return m_Num1 - m_Num2;
        }
        else if (oper == "*")
        {
            return m_Num1 * m_Num2;
        }
        else if (oper == "/")
        {
            return m_Num1 / m_Num2;
        }
        //如果想扩展新功能,需要修改源码
        //在真实开发中 提倡 开闭原则
        //开闭原则:对扩展进行开放,对修改进行关闭
    }
    int m_Num1;  //操作数1
    int m_Num2;  //操作数2
};

//多态技术
//多态好处:1、组织结构清晰   2、可读性强  3、对于前后期的维护和扩展高
//实现计算器抽象类
class AbstractCalculator
{
public:
    virtual int getResult()
    {
        return 0;
    }
    int m_Num1;
    int m_Num2;
};

//加法计算器类
class AddCalculator :public AbstractCalculator
{
public:
    virtual int getResult()    //   virtual可加可不加
    {
        return m_Num1 + m_Num2;
    }
};
//减法计算器类
class SubCalculator :public AbstractCalculator
{
public:
    int getResult()    //   virtual可加可不加
    {
        return m_Num1 - m_Num2;
    }
};
//乘法计算器类
class MulCalculator :public AbstractCalculator
{
public:
    int getResult()    //   virtual可加可不加
    {
        return m_Num1 * m_Num2;
    }
};
//除法计算器类
class ChuCalculator :public AbstractCalculator
{
public:
    int getResult()    //   virtual可加可不加
    {
        return m_Num1 / m_Num2;
    }
};

void test1()
{
    Calculator c;   //创建一个计算器对象
    c.m_Num1 = 10;
    c.m_Num2 = 10;

    cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
    cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
    cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
    cout << c.m_Num1 << "/" << c.m_Num2 << "=" << c.getResult("/") << endl;
}

void test2()
{
    //多态使用条件
    //父类指针或者引用指向子类对象

    //加法运算
    AbstractCalculator* a = new AddCalculator;
    a->m_Num1 = 100;
    a->m_Num2 = 100;
    cout << a->m_Num1 << "+" << a->m_Num2 << "=" << a->getResult() << endl;
    //用完后记得销毁
    delete a;

    //减法运算
    a = new SubCalculator;
    a->m_Num1 = 100;
    a->m_Num2 = 100;
    cout << a->m_Num1 << "-" << a->m_Num2 << "=" << a->getResult() << endl;
    //用完后记得销毁
    delete a;

    //乘法运算
    a = new MulCalculator;
    a->m_Num1 = 100;
    a->m_Num2 = 100;
    cout << a->m_Num1 << "*" << a->m_Num2 << "=" << a->getResult() << endl;
    //用完后记得销毁
    delete a;

    //减法运算
    a = new ChuCalculator;
    a->m_Num1 = 100;
    a->m_Num2 = 100;
    cout << a->m_Num1 << "/" << a->m_Num2 << "=" << a->getResult() << endl;
    //用完后记得销毁
    delete a;
}

int main()
{
    //test1();
    test2();
    system("pause");
    return 0;
}
复制代码

4.7.3 纯虚函数和抽象类

       在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数1改为纯虚函数。

  纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0;

当类中有了纯虚函数,这个类也称为抽象类。

 

抽象类特点:

1、无法实例化对象

2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类

复制代码
#include<iostream>
using namespace std;

class Base
{
public:

    //纯虚函数
    //只要有一个纯虚函数,这个类称为抽象类
    //抽象类特点:
    //1、无法实例化对象
    //2、抽象的子类必须重写父类中的纯虚函数,否则也属于抽象类
    virtual void func() = 0;
};

class Son :public Base
{
public:
    virtual void func() 
    {
        cout << "func函数调用" << endl;
    };
};

void test1()
{
    //Base b;    //抽象类是无法实例化对象
    //new Base;    //抽象类是无法实例化对象

    Son s;    //子类必须重写父类中的纯虚函数,否则无法实例化对象
    
    Base* base = new Son;
    base->func();

}
int main()
{
    test1();
    system("pause");
    return 0;
}
复制代码

 案例 做饮料

复制代码
#include<iostream>
using namespace std;

//多态案例2 制作饮品
class AbstractDrinking
{
public:
    virtual void Boil() = 0;            //煮水
    virtual void Brew() = 0;            //冲泡
    virtual void PourInCup() = 0;        //倒入杯中
    virtual void PutSomething() = 0;    //加入佐料
    void MakeDrink()        //制作饮品
    {
        Boil();
        Brew();
        PourInCup();
        PutSomething();
    }
};

//制作咖啡
class Coffee :public AbstractDrinking
{
public:
    //煮水
    virtual void Boil()
    {
        cout << "煮农夫山泉" << endl;
    }
    //冲泡
    virtual void Brew()
    {
        cout << "冲泡咖啡" << endl;
    }
    //倒入杯中
    virtual void PourInCup()
    {
        cout << "将咖啡倒入杯中" << endl;
    }
    //加入佐料
    virtual void PutSomething()
    {
        cout << "加入糖和牛奶" << endl;
    }

};

//制作茶叶水
class Tea :public AbstractDrinking
{
public:
    //煮水
    virtual void Boil()
    {
        cout << "煮崂山泉水" << endl;
    }
    //冲泡
    virtual void Brew()
    {
        cout << "冲泡铁观音" << endl;
    }
    //倒入杯中
    virtual void PourInCup()
    {
        cout << "将茶水倒入杯中" << endl;
    }
    //加入佐料
    virtual void PutSomething()
    {
        cout << "加入枸杞" << endl;
    }

};

//制作
void doWork(AbstractDrinking * abs)   //AbstractDrinking * abs = new Coffee
{
    abs->MakeDrink();
    delete abs;  //堆区内存手动开辟,手动释放
}

void test1()
{
    //制作咖啡
    doWork(new Coffee);
    cout << "-----------------" << endl;
    doWork(new Tea);

}

int main()
{
    test1();
    system("pause");
    return 0;
}
复制代码

 

4.7.5 虚析构和纯虚析构

  多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决办法:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

1、可以解决父类指针释放子类对象

2、都需要具体的函数实现

区别:

如果是纯虚析构,该类属于抽象类,无法实例化对象

 

虚析构语法: virtual ~类名(){}

纯虚析构语法:virtual ~类名() = 0

复制代码
#include<iostream>
#include<string>
using namespace std;

//虚析构和纯虚析构
class Animal
{
public:
    Animal()
    {
        cout << "Animal 构造函数的调用" << endl;
    }
    ////利用虚析构可以解决 父类指针释放子类对象时不干净的问题
    //virtual ~Animal()
    //{
    //    cout << "Animal 析构函数的调用" << endl;
    //}

    //纯虚析构  需要声明也需要实现
    //有了纯虚析构之后 这个类也属于抽象类 无法实例化对象
    virtual ~Animal() = 0;   //如果没有第27-30行的类外声明,代码将无法运行  因为没实现就不能执行父类的析构


    //纯虚函数
    virtual void speak() = 0;
};

Animal::~Animal()
{
    cout << "Animal 纯虚析构函数的调用" << endl;
}

class Cat :public Animal
{
public:
    Cat(string name)
    {
        cout << "Cat构造函数的调用" << endl;
        m_Name = new string(name);
    }
    virtual void speak()
    {
        cout << *m_Name << "小猫在说话" << endl;
    }

    ~Cat()
    {
        if (m_Name != NULL)
        {
            cout << "Cat析构函数调用" << endl;
            delete m_Name;
            m_Name = NULL;
        }
    }

    string *m_Name;
};

void test1()
{
    Animal * animal = new Cat("Tom");
    animal->speak();
    //父类指针在析构时 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏,解决办法为把父类中的析构函数改为虚析构
    delete animal;
}

int main()
{
    test1();
    system("pause");
    return 0;
}
复制代码

 案例2 电脑组装

 

posted @   大白不会敲代码  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示