【贪玩巴斯】c++核心编程,你需要敲出这些程序 !(七){ 多态 全解 详解!} //2021-05-14

//

//  main.cpp

//  _day7

//

//  Created by AchesonD16 贪玩巴斯 on 2021/5/7.

//

 

 

// 多态

 

 

一、多态的基本概念

// 多态的基本概念

//多态是C++ 面相对象三大特性之一

/*

 

多态分为两类

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

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

 

静态多态和动态多态区别:

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

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

*/

 

//例子

/*

#include<iostream>

using namespace std;

 

class Animal

{

public:

    //Speak函数的声明前加上 virtual 关键字 就为虚函数

    //函数前面加上virtual关键字,变为虚函数。那么编译器在编译的时候就不能确定函数调用了。

    virtual void speak()

    {

        cout << "动物类在说话" << endl;

    }

};

 

class Cat : public Animal

{

public:

    void speak()

    {

        cout << "小猫类在说话" << endl;

    }

};

 

class Dog : public Animal

{

public:

    void speak()

    {

        cout << "小狗类在说话" << endl;

    }

};

// 我们希望传入什么对象,那么就调用什么对象的函数

// 如果函数地址在编译阶段就能确定,那么就是静态 多态

// 如果函数地址在运行阶段才能确定,那么就是动态 多态

 

void Dospeak(Animal& animal)

{

    animal.speak();

}

二、多态成立的条件和使用方法

// 多态成立满足的条件:

// 1、有继承关系

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

 

// 多态的使用方法:

// 父类的 指针 或者 引用 指向 子类对象

 

void test1()

{

    Cat c;

    Dospeak(c); // 子类父类之间可以直接互相调用

    

    Dog d;

    Dospeak(d);

}

 

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

小猫类在说话

小狗类在说话

总结:

多态满足的条件:

子类、父类有继承的关系。

子类要 重写 父类中的虚函数

 

多态使用的条件:

必须要用

父类 指针 或 引用 必须 指向 子类对象

 

重写:

函数返回值类型 函数名 形参列表 必须完全一致,才叫做重写

*/

 

 

三、多态案例一-计算器类

// 多态案例一 - 计算器类

//案例描述:

//分别利用普通写法 和 多态技术, 设计实现两个操作数 进行运算的 计算器类

 

//多态的优点:

//1.代码组织结构清晰

//2.可读性强

//3.利于前期和后期的扩展以及维护

 

//示例:

/*

#include <iostream>

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;

        }

        // 如果要提供新的运算,就需要修改最初的源码

        // 我的编译器这里会报错

    }   // non-void function does not return a value in all control 这句话的意思是函数并不是所有分支都有返回值。

public:

    int m_Num1;

    int 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;

}

// 多态实现

// 多态实现

// 抽象计算器类

// 多态优点:代码组织结构清晰,可读性强,利于后期和前期的扩展以及维护

 

 

//抽象计算器类

class AbstractCalculator

{

public:

    virtual int getResult()

    {

        return 0;

    }

    

    int m_Num1;

    int m_Num2;

};

 

//加法计算器

class AddCalculator : public AbstractCalculator

{

public:

    int getResult()

    {

        return m_Num1 + m_Num2;

    }

};

 

//减法计算器

class SubCalculator : public AbstractCalculator

{

public:

    int getResult()

    {

        return m_Num1 - m_Num2;

    }

};

 

//乘法计算器类

class MulCalculator : public AbstractCalculator

{

public:

    int getResult()

    {

        return m_Num1 * m_Num2;

    }

};

 

 

void test2()

{

    //创建加法计算器

    AbstractCalculator* abc = new AddCalculator;

    abc->m_Num1 = 10;

    abc->m_Num2 = 10;

    

    cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;

    delete abc;  // new:堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete。 用完了要销毁

 

    

    //创建减法计算器

    abc = new SubCalculator;

    abc->m_Num1 = 10;

    abc->m_Num2 = 10;

    cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;

    delete abc;

    

    //创建乘法计算器

    abc = new MulCalculator;

    abc->m_Num1 = 10;

    abc->m_Num2 = 10;

    

    cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;

    delete abc;

 

}

 

int main()

{

    //test1();

    test2();

    

    system("pause");

    return 0;

}

结果:

10 + 10 = 20

10 - 10 = 0

10 * 10 = 100

总结:

C++ 开发提倡利用多态设计程序架构,因为多态优点很多

 

*/

 

 

 

 

 

四、纯虚函数和抽象类

 

/*

一、纯虚函数和抽象类:

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将 虚函数 改为 纯虚函数

 

二、纯虚函数语法:

virtual 返回值类型 函数名(形参列表)= 0;

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

 

三、抽象类的特点:

无法实例化对象

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

*/

 

 

/*

#include<iostream>

using namespace std;

 

class Base

{

public:

    //纯虚函数

    //类中只要有一个纯虚函数就称为抽象类

    //抽象类无法实例化对象

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

    virtual void func() = 0;  //语法

    

};

 

class Son : public Base

{

public:

    virtual void func()

    {

        cout << "func调用" << endl;

    }

};

 

void test1()

{

    Base* b = NULL;

    //b = new Base; //错误,抽象类无法实例化对象

    b = new Son;

    b->func();

    delete b; //记得销毁

}

 

int main()

{

    test1();

    

    system("pause");

    

    return 0;

}

结果:

func调用

*/

 

 

 

 

五、多态案例二

/*

多态案例二 - 制作饮品

案例描述:

制作饮品的大致流程为: 煮水 - 冲泡 - 倒入杯中 - 加入辅料

利用多态技术实现本案例,提供 抽象制作饮品基类,提供子类制作咖啡和茶叶

 

 

 

#include <iostream>

using namespace std;

 

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* drink)

{

    drink->MakeDrink();

    delete drink;

}

 

void test1()

{

    DoWork(new Coffee);

    cout << "---------------" << endl;

    DoWork(new Tea);

}

 

 

int main()

{

    test1();

    system("pause");

    return 0;

}

结果:

煮农夫山泉!

冲泡咖啡!

将咖啡倒入杯中!

加入牛奶!

---------------

煮自来水!

冲泡茶叶!

将茶水倒入杯中!

加入枸杞!

*/

 

 

 

 

六、虚析构和纯虚虚构

// 虚析构和纯虚析构

/*

一、定义

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

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

 

二、虚析构和纯虚析构的共性:

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

都需要具体的函数实现

 

三、虚析构和纯虚析构区别:

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

 

四、

虚析构语法:

virtual ~类名()

{}

五、

纯虚析构语法:

virtual ~类名() = 0;

在类外定义

类名::~类名()

{}

 

 

 

 

#include<iostream>

using namespace std;

#include<string>

 

class Animal

{

public:

    Animal()

    {

        cout << "Animal的构造函数!" << endl;

    }

    

    virtual void Speak() = 0;

    

    

//    虚析构的定义方法

//    //析构函数加上virtual关键字,变成 虚析构函数

//    virtual ~Animal()

//    {

//        cout << "Animal 虚析构函数调用!" << endl;

//    }

    

//    纯虚析构的定义方法

    virtual ~Animal() = 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()

    {

        cout << "Cat析构函数的调用!" << endl;

        if( this->m_Name  !=  NULL )

        {

            delete m_Name;

            m_Name = NULL;

        }

    }

public:

    string *m_Name;

};

 

void test1()

{

    Animal *animal = new Cat("Tom");

    animal->Speak();

    

    //通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏

    //怎么解决?给基类增加一个 虚析构函数

    //虚析构函数  就是用来解决通过父类指针释放 子类对象

    delete animal;

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

结果:

Animal的构造函数!

Cat构造函数调用!

Tom小猫在说话!

Cat析构函数的调用!

Animal 纯虚析构的函数调用!

总结:

1、虚析构 或 纯虚析构 就是用来解决 通过父类指针 释放 子类对象

2、如果子类中没有使用 堆区, 可以不写 虚析构 或 纯虚析构

3、拥有 纯虚析构函数 的类也 属于 抽象类

 

*/

 

 

七、多态案例三

//案例:多态案例三 - 电脑组装

/*

案例描述:

电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)

将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作

*/

 

 

/*

#include <iostream>

using namespace std;

#include <string>

 

//抽象CPU类

class CPU

{

public:

    //抽象的计算函数

    virtual void calculate() = 0;

};

 

//抽象显卡类

class VideoCard

{

public:

    //抽象的显示函数

    virtual void display() = 0;

};

 

//抽象内存条类

class Memory

{

public:

    //抽象的存储函数

    virtual void storage() = 0;

};

 

//电脑类

class Computer

{

public:

    Computer(CPU* cpu, VideoCard* vc, Memory* mem)

    {

        m_cpu = cpu;

        m_vc = vc;

        m_mem = mem;

    }

    

    //提供工作的函数

    void work()

    {

        //让零件工作起来,调用接口

        m_cpu->calculate();

        m_vc->display();

        m_mem->storage();

    }

    

    //提供析构函数 释放3个电脑零件

    ~Computer()

    {

        //释放CPU零件

        if(m_cpu != NULL)

        {

            delete m_cpu;

            m_cpu = NULL;

        }

        

        //释放显卡零件

        if(m_vc != NULL)

        {

            delete m_vc;

            m_vc = NULL;

        }

        

        //释放内存条零件

        if(m_mem != NULL)

        {

            delete m_mem;

            m_mem = NULL;

        }

    }

    

private:

    CPU* m_cpu;   //cpu的零件指针

    VideoCard* m_vc;    //显卡零件指针

    Memory* m_mem;  //内存条零件指针

};

 

 

//具体厂商

//Intel厂商

 

class IntelCPU : public CPU

{

public:

    virtual void calculate()

    {

        cout << "Intel的CPU开始计算了!" << endl;

    }

};

 

class IntelVideoCard : public VideoCard

{

public:

    virtual void display()

    {

        cout << "Intel的显卡开始显示了!" << endl;

    }

};

 

class IntelMemory : public Memory

{

public:

    virtual void storage()

    {

        cout << "Intel的内存条开始存储了!" << endl;

    }

};

 

 

//Lenovo厂商

class LenovoCPU : public CPU

{

public:

    virtual void calculate()

    {

        cout << "Lenovo的CPU开始计算了!" << endl;

    }

};

 

class LenovoVideoCard : public VideoCard

{

public:

    virtual void display()

    {

        cout << "Lenovo的显卡开始显示了!" << endl;

    }

};

 

class LenovoMemory : public Memory

{

public:

    virtual void storage()

    {

        cout << "Lenovo的内存条开始存储了!" << endl;

    }

};

 

void test1()

{

    //第一台电脑零件

    CPU* intelcpu = new IntelCPU;

    VideoCard* intelvid = new IntelVideoCard;

    Memory* intelmem = new IntelMemory;

    

    cout << "第一台电脑开始工作:" << endl;

    //创建第一台电脑

    Computer* com1 = new Computer(intelcpu, intelvid, intelmem);

    com1->work();

    delete com1;

    

    cout << "----------------------------" << endl;

    cout << "第二台电脑开始工作:" << endl;

    //第二台电脑组装

    Computer* com2 = new Computer(new LenovoCPU,new LenovoVideoCard, new LenovoMemory);

    com2->work();

    delete com2;

    

    cout << "----------------------------" << endl;

    cout << "第三台电脑开始工作:" << endl;

    //第三台电脑组装

    Computer* com3 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);

    com3->work();

    delete com3;

    

}

 

int main()

{

    test1();

    

    system("pause");

    return 0;

}

 

结果:

第一台电脑开始工作:

Intel的CPU开始计算了!

Intel的显卡开始显示了!

Intel的内存条开始存储了!

----------------------------

第二台电脑开始工作:

Lenovo的CPU开始计算了!

Lenovo的显卡开始显示了!

Lenovo的内存条开始存储了!

----------------------------

第三台电脑开始工作:

Lenovo的CPU开始计算了!

Lenovo的显卡开始显示了!

Lenovo的内存条开始存储了!

 

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贪玩巴斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值