C++学习笔记“类和对象”:多态;

目录

4.7 多态

4.7.1 多态的基本概念

 4.7.2 多态案例--计算器类

4.7.3 纯虚函数和抽象类 

4.7.4 多态案例二 - 制作饮品

4.7.5 虚析构和纯虚析构

4.7.6 多态案例三-电脑组装 


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. 父类有一个虚函数
// 3. 子类重写了父类的虚函数

// 动态多态使用
// 父类的指针或引用指向子类的对象,调用父类的函数,实际调用的是子类的函数

//执行说话的函数
void speak(Animal& animal) {// 这里的Animal&参数表示传入的是Animal类的引用
	animal.speak();// 调用Animal类的speak()函数
}

void test() {
	Cat cat;// 实例化一个猫对象
	Dog dog;// 实例化一个狗对象

	speak(cat); // 输出 "猫说话"
	speak(dog); // 输出 "狗说话"
}

int main() {
	test();

	return 0;
}

总结:
多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数

多态便用条件

  • 父类指针或引用指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

 4.7.2 多态案例--计算器类

案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:

  • 代码组织结构清晰
  • 可读性强
  • 利于前期和后期的扩展以及维护

示例:

#include <iostream>
using namespace std;

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

// 普通写法
class Calculator {
public:
    int getResult(string oper) {
        if (oper == "+") {
            return num_1 + num_2;
        }
        else if (oper == "-") {
            return num_1 - num_2;
        }
        else if (oper == "*") {
            return num_1 * num_2;
        }
        else if (oper == "/") {
            return num_1 / num_2;
        }
        else {
            cout << "Invalid operator!" << endl;
            return 0;
        }
    }

    void setNum1(int num) {
        num_1 = num;
    }

    void setNum2(int num) {
        num_2 = num;
    }

    int num_1;
    int num_2;
};

void testCalculator() {
    Calculator c;
    c.setNum1(10);
    c.setNum2(5);
    cout << "10 + 5 = " << c.getResult("+") << endl;
    cout << "10 - 5 = " << c.getResult("-") << endl;
    cout << "10 * 5 = " << c.getResult("*") << endl;
    cout << "10 / 5 = " << c.getResult("/") << endl;
    cout << "10 % 5 = " << c.getResult("%") << endl;
    cout << "------------------------------------- "  << endl;
}

// 利用多态实现计算器
// 多态好处
// 1、组织代码更加清晰
// 2、可读性更好
// 3、可扩展性更强,维护性更好

// 定义计算器基类
class CalculatorBase {
public:
    virtual int getResult(){ return 0; }
    int num_1;
    int num_2;
};

// 定义加法类
class Add : public CalculatorBase {
public:
    int getResult() {return num_1 + num_2;}
};

// 定义减法类
class Subtract : public CalculatorBase {
public:
    int getResult() {return num_1 - num_2;}
};

// 定义乘法类
class Multiply : public CalculatorBase {
public:
    int getResult() {return num_1 * num_2;}
};

// 定义除法类
class Divide : public CalculatorBase {
public:
    int getResult() {return num_1 / num_2;}
};

// 测试多态计算器
void testCalculator_2() {
    CalculatorBase *c1 = new Add();
    c1->num_1 = 10;
    c1->num_2 = 5;
    cout << "10 + 5 = " << c1->getResult() << endl;

    CalculatorBase *c2 = new Subtract();
    c2->num_1 = 10;
    c2->num_2 = 5;
    cout << "10 - 5 = " << c2->getResult() << endl;

    CalculatorBase *c3 = new Multiply();
    c3->num_1 = 10;
    c3->num_2 = 5;
    cout << "10 * 5 = " << c3->getResult() << endl;

    CalculatorBase *c4 = new Divide();
    c4->num_1 = 10;
    c4->num_2 = 5;
    cout << "10 / 5 = " << c4->getResult() << endl;

    delete c1;
    delete c2;
    delete c3;
    delete c4;
}

// 程序入口
int main() {
    testCalculator();
    testCalculator_2();

    return 0;
}

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

4.7.3 纯虚函数和抽象类 

 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法: virtual 返回值类型 函数名(参数列表)=0;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

示例:

#include <iostream>
using namespace std;

// 纯虚函数和抽象类

class Base {
public:
    // 只要有一个纯虚函数,该类就是抽象类
    // 不能创建对象,只能作为基类被继承
    // 抽象类特点
    // 1. 无法实例化对象
    // 2. 抽象类的子类,必须要重写父类的纯虚函数,否则子类也是抽象类
    virtual void fun() = 0; // 纯虚函数
};

class Derived : public Base {
public:
    virtual void fun() {
        cout << "Derived::fun()" << endl;
    }
};

void test() {
    //Base b; // 抽象类不能创建对象
    // Base b1; // 抽象类不能实例化
    // Base b2 = new Base; // 抽象类不能实例化

    Derived d;// 继承抽象类,必须重写父类的纯虚函数
    d.fun();

    Base *base = new Derived; // 指针指向抽象类的子类对象,可以调用子类的成员函数
    base->fun(); // 调用子类的成员函数
}

int main() {
    test();
    return 0;
}
4.7.4 多态案例二 - 制作饮品

案例描述:
制作饮品的大致流程为:煮水-冲泡-倒入杯中-加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#include <iostream>
#include <string>
using namespace std;

// 制作饮品的基类
class Drink {
public:
    virtual void BoilWater() = 0;// 烧水
    virtual void AddCondiments() = 0;// 添加调料
    virtual void PourInCup() = 0;// 把饮品倒进杯子
    virtual void AddSugar() = 0;// 添加糖
    virtual void AddMilk() = 0;// 添加牛奶
    void makeDrink(){
        BoilWater();
        AddCondiments();
        PourInCup();
        AddSugar();
        AddMilk();
    }
};

// 制作咖啡的子类
class Coffee : public Drink {
public:
    void BoilWater() {
        cout << "烧水" << endl;
    }
    void AddCondiments() {
        cout << "添加糖" << endl;
    }
    void PourInCup() {
        cout << "倒进杯子" << endl;
    }
    void AddSugar() {
        cout << "添加糖" << endl;
    }
    void AddMilk() {
        cout << "添加牛奶" << endl;
    }
};

// 制作茶的子类
class Tea : public Drink {
public:
    void BoilWater() {
        cout << "烧水" << endl;
    }
    void AddCondiments() {
        cout << "添加柠檬" << endl;
    }
    void PourInCup() {
        cout << "倒进杯子" << endl;
    }
    void AddSugar() {
        cout << "添加糖" << endl;
    }
    void AddMilk() {
        cout << "添加牛奶" << endl;
    }
};

// 制作饮品的函数
void makeDrinkWork(Drink* drink) {
    drink->makeDrink();// 调用基类的makeDrink()函数
    cout << "制作--完成" << endl;
    delete drink;// 释放内存
}

void test() {
    // 制作咖啡
    makeDrinkWork(new Coffee());// 动态创建咖啡对象并调用makeDrinkWork()函数

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

    // 制作茶
    makeDrinkWork(new Tea());
}

int main() {
    test();

    return 0;
}
4.7.5 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

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

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

#include <iostream>
#include <string>
using namespace std;

// 虚析构和纯虚析构

class Animal {
public:
    virtual void speak() = 0; // 纯虚函数
    // 利用虚析构函数释放内存,可以解决父类指针释放子类对象时不干净的问题
    //virtual ~Animal() {} // 虚析构函数,释放内存

    // 纯虚析构函数,需要声明也需要实现
    // 有了纯虚析构函数,这个类也就成为了抽象类,不能实例化对象,只能作为基类被派生
    virtual ~Animal() = 0; // 纯虚析构函数,声明,但不定义,用于派生类中
};

// 纯虚析构函数,用于派生类中,声明但不定义
Animal::~Animal(){}

class Cat : public Animal {
public:
    Cat(string *name) {
        m_Name = new string(*name);
    }
    virtual void speak() {
        cout << "Cat: " << *m_Name << ";Meow!" << endl;
    }
    ~Cat() { cout << "Cat: " << *m_Name << ";Bye!" << endl; delete m_Name; } // 虚析构函数,释放内存
    string *m_Name;
};

void test_Cat() {
    string name = "Kitty";// 注意:这里传递的是指针,而不是字符串
    Animal* a = new Cat(&name);// 注意:这里传递的是指针,而不是对象
    a->speak();// 注意:这里调用的是虚函数,而不是对象指针
    delete a;// 注意:这里释放的是指针,而不是对象
}

int main() {
    test_Cat();
    return 0;
}

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

2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

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

4.7.6 多态案例三-电脑组装 

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

将每个零件封装出抽象其类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

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

#include <iostream>
#include <string>
using namespace std;

// CPU类
class CPU {
public:
    virtual void calculate() = 0;// 虚函数,用于计算功能
};

// 主板类
class Mainboard {
public:
    virtual void motherboard() = 0;// 虚函数,用于组装主板
};

// 内存条类
class Memory {
public:
    virtual void RAM() = 0;// 虚函数,用于安装内存条
};

// 显卡类
class GraphicsCard {
public:
    virtual void display() = 0;// 虚函数,用于安装显卡
};

// 电脑类
class Computer {
public:
    Computer(CPU* cpu, Mainboard* mainboard, Memory* memory, GraphicsCard* graphicsCard)
        : cpu(cpu), mainboard(mainboard), memory(memory), graphicsCard(graphicsCard) {// 构造函数,传入各个部件对象
        /*cpu = cpu;
        mainboard = mainboard;
        memory = memory;
        graphicsCard = graphicsCard;*/
    }

    void assemble() {// 组装电脑
        cpu->calculate();
        mainboard->motherboard();
        memory->RAM();
        graphicsCard->display();
    }

private:
    CPU* cpu;// CPU对象
    Mainboard* mainboard;// 主板对象
    Memory* memory;// 内存条对象
    GraphicsCard* graphicsCard;// 显卡对象
};

// 具体实现类
class IntelCPU : public CPU {
public:
    virtual void calculate() {
        cout << "Intel CPU is calculating..." << endl;
    }
};

class ASUSMainboard : public Mainboard {
public:
    virtual void motherboard() {
        cout << "ASUS Mainboard is assembling..." << endl;
    }
};

class KingstonMemory : public Memory {
public:
    virtual void RAM() {
        cout << "Kingston Memory is installing..." << endl;
    }
};

class NvidiaGraphicsCard : public GraphicsCard {
public:
    virtual void display() {
        cout << "Nvidia GraphicsCard is installing..." << endl;
    }
};

void test() {
    // 第一台电脑零件
    CPU* intelCPU = new IntelCPU();
    Mainboard* asusMainboard = new ASUSMainboard();
    Memory* kingstonMemory = new KingstonMemory();
    GraphicsCard* nvidiaGraphicsCard = new NvidiaGraphicsCard();

    // 组装电脑
    Computer* computer = new Computer(intelCPU, asusMainboard, kingstonMemory, nvidiaGraphicsCard);
    computer->assemble();

    // 释放内存
    delete intelCPU;
    delete asusMainboard;
    delete kingstonMemory;
    delete nvidiaGraphicsCard;
    delete computer;
}

int main() {
    test();
    return 0;
}

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值