多态介绍

面向对象三大特性之一, 多态分类

  • 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名
  • 动态多态:派生类 和 基类 实现运行时多态 静态动态多态区别:
  • 多态的函数地址早绑定——编译阶段确定函数地址
  • 多态的函数地址晚绑定——运行阶段确定函数地址
 
class Animal
{
public:
    virtual void speak()//虚函数    //多态核心1virtual 地址晚绑定——运行阶段绑定
        {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();}///多态核心2Animal &animal = [cat/dog];
 
int main()
{
    Cat cat;
    Dog dog;
    doSpeak(cat);
    dospeak(dog);
    //output:小猫在说话
    //小狗在说话
    return 0;
}
title:Example
collapse:open
class Animal
{
public:
    void speak()
        {cout << "动物在说话" << endl;}
};
 
class Cat:public Animal
{
public:
    void speak()
        {cout << "小猫在说话" << endl;}
};
 
class 
 
void doSpeak(Animal &animal)
    {animal.speak();}//Animal &animal = cat;
 
int main()
{
    Cat cat;
    doSpeak(cat);
    //output:动物在说话
    return 0;
}
title:Summary
collapse:open
~~~ad-example
title:重载 
collapse:open
同一作用域(同一文件)    
 
函数名相同    
 
函数参数类型不同,个数不同,顺序不同    
 
与函数返回值类型无关    
~~~
<br>
<br>
 
~~~ad-example
title:重写
collapse:open
通常为子类重写父类虚函数
 
函数返回值类型  函数名  参数列表  完全相同
 
- 重写是与多态相关的应用方式
 
父类指针或者引用  执行子类对象
~~~

原理简单刨析

多态原理图解

⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠

Text Elements

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

vfptr - 虚函数指针 v - virtual f - function ptr - pointer

Animal 内部结构 vfptr

vftable

vftable - 虚函数表 v - virtual f - function table - table

&Animal::speak

class Cat:public Animal { public: virtual void speak() {cout << “小猫在说话” << endl;} };

Cat 内部结构重写Animal之后 vfptr

vftable

&Cat::speak

Embedded files

0e79c74740a2ce26c3070c8007fb1928a4203136: 4b9af544de6ca83bebe9f4c04965f89600c97621:

指向原始笔记的链接

具体案例

cpp多态案例

title:猫狗说话
collapse:open
```cpp
#include <iostream>
using namespace std;
 
class Animal
{
public:
    virtual void speak()//虚函数    //多态核心1virtual 地址晚绑定——运行阶段绑定
        {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();}///多态核心2Animal &animal = [cat/dog];
 
int main()
{
    Cat cat;
    Dog dog;
    doSpeak(cat);
    dospeak(dog);
    //output:小猫在说话
    //小狗在说话
    return 0;
}
title:不利用多态
collapse:open
设计计算器
```cpp
#include<iostream>
using namespace std;
 
enum TYPE
{
    ADD,
    DEC,
    MULT,
    DIV
};
 
class Calculator
{
public:
    int getResult(std::string oper)
    {
        switch(oper)
        {
        case '+':return m_num1 + m_num2;
        case '-':return m_num1 - m_num2;
        case '*':return m_num1 * m_num2;
        case '/':return m_num1 / m_num2;
        default:break;
        }
    }
 
private:
    int m_num1, m_num2;
};
 
int main()
{
    Calculator c;
    c.m_num1 = 10;
    c.m_num2 = 10;
    cout << c.m_num1 << '+' << c.m_num2 << '=' << c.getResult("+") << endl;
    //output:10+10=20
    return 0;
}
title:利用多态设计计算器
collapse:open
```cpp
#include <iostream>
using namespace std;
 
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 multCalculator :public AbstractCalculator
{
public:
    int getResult(){return m_num1 * m_num2;}
    
};
 
class divCalculator :public AbstractCalculator
{
public:
    int getResult(){return m_num1 / m_num2;}
    
};
 
int main()
{
    AbstractCalculator *abc = new addCalculator;
    abc->m_num1 = 100;
    abc->m_num2 = 100;
    cout << abc->getResult() << endl;
    delete abc;
    
    abc = new subCalculator;
    abc->m_num1 = 100;
    abc->m_num2 = 100;
    cout << abc->getResult() << endl;
    delete abc;
    
    abc = new multCalculator;
    abc->m_num1 = 100;
    abc->m_num2 = 100;
    cout << abc->getResult() << endl;
    delete abc;
    
    abc = new divCalculator;
    abc->m_num1 = 100;
    abc->m_num2 = 100;
    cout << abc->getResult() << endl;
    delete abc;
    /*output:
    200
    0
    10000
    1
    */
}
title:制作饮品
collapse:open
```cpp
#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:
    void boil() /// 煮水
    {
        cout << "煮农夫山泉" << endl;
    }
    void brew() /// 冲泡
    {
        cout << "冲泡咖啡" << endl;
    }
    void pourInCup() /// 倒入杯中
    {
        cout << "倒入杯中" << endl;
    }
    void putSomething() /// 加入辅料
    {
        cout << "加入糖和牛奶" << endl;
    }
    void makeDrink()
    {
        boil();
        brew();
        pourInCup();
        putSomething();
    }
};
 
class Tea: public AbstractDrinking
{
public:
    void boil() /// 煮水
    {
        cout << "煮农夫山泉" << endl;
    }
    void brew() /// 冲泡
    {
        cout << "冲泡茶" << endl;
    }
    void pourInCup() /// 倒入杯中
    {
        cout << "倒入杯中" << endl;
    }
    void putSomething() /// 加入辅料
    {
        cout << "加入枸杞" << endl;
    }
    void makeDrink()
    {
        boil();
        brew();
        pourInCup();
        putSomething();
    }
};
 
//制作函数
void doWork(AbstractDrinking* abs)
{
    abs->makeDrink();
    delete abs;
}
 
int main()
{
    doWork(new Coffee);
    /*output:
    煮农夫山泉
    冲泡咖啡
    倒入杯中
    加入糖和牛奶*/
    cout << "-------------" << endl;
    doWork(new Tea);
    /*output:
    -------------
    煮农夫山泉
    冲泡茶
    倒入杯中
    加入枸杞
    */
    return 0;
}
title:多态电脑
collapse:open
 
```cpp
#include <iostream>
using namespace std;
 
class CPU
{
public:
    virtual void calculate() = 0;
};
 
class GPU
{
public:
    virtual void display() = 0;
};
 
class RAM
{
public:
    virtual void storage() = 0;
};
 
class Computer
{
public:
    Computer(CPU* cpu, GPU* gpu, RAM* ram)
    {
        m_cpu = cpu;
        m_gpu = gpu;
        m_ram = ram;
    }
 
    void work()
    {
        m_cpu->calculate();
        m_gpu->display();
        m_ram->storage();
    }
    
    ~Computer()
    {
        if(m_cpu != NULL)
        {
            delete m_cpu;
            m_cpu = NULL;
        }
        
        if(m_gpu != NULL)
        {
            delete m_gpu;
            m_gpu = NULL;
        }
        
        if(m_ram != NULL)
        {
            delete m_ram;
            m_ram = NULL;
        }
    }
    
private:
    CPU* m_cpu;
    GPU* m_gpu;
    RAM* m_ram;
};
 
//Inter
class IntelCPU: public CPU
{
public:
    virtual void calcutale(){cout << "IntelCPUstartCalculate" << endl;}
};
 
class IntelGPU: public GPU
{
public:
    virtual void display(){cout << "IntelGPUstartDisplay" << endl;}
};
 
class IntelRAM: public RAM
{
public:
    virtual void storage(){cout << "IntelRAMstartStorage" << endl;}
};
 
//Lenovo
class LenovoCPU: public CPU
{
public:
    virtual void calcutale(){cout << "LenovoCPUstartCalculate" << endl;}
};
 
class LenovoGPU: public GPU
{
public:
    virtual void display(){cout << "LenovoGPUstartDisplay" << endl;}
};
 
class LenovoRAM: public RAM
{
public:
    virtual void storage(){cout << "LenovoGPUstartStorage" << endl;}
};
 
void test01()
{
    
    CPU* intelcpu = new IntelCPU;
    GPU* intelgpu = new IntelGPU;
    RAM* intelram = new IntelRAM;
    
    //组装第一台电脑
    Computer* computer1 = new Computer(intelcpu, intelgpu, intelram);
    conputer1->work();
    delete conputer1;
    
    //组装第二台电脑
    Computer* computer2 = new Computer(
    new LenovoCPU, new LenovoGPU, new LenovoRAM
    );
    conputer2->work();
    delete conputer2;
    
    //组装第三台电脑
    Computer* computer3 = new Computer(
    new LenovoCPU, new IntelGPU, new LenovoRAM
    );
    conputer3->work();
    delete conputer3;
}
 
int main()
{
    test01();
    return 0;
}
指向原始笔记的链接

title:多态好处
collapse:open
- 组织结构逻辑清晰
- 可读性强
- 对于后期的扩展和可维护性高

纯虚函数和抽象类(接口类)

纯虚函数 virtual 返回值类型 函数名(参数列表) = 0; 抽象类 当类中有了纯虚函数,这个类也称为抽象类(接口类)

title:抽象类特点
- 无法实例化对象(创建不了)
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
title:作用
- 编译器约束必须有子类实现
 
- 运行时多态
 
- 解耦
title:Important
collapse:open
```cpp
class Base
{
public:
    //纯虚函数
    virtual void func() = 0;
};
title:Error
collapse:open
```cpp
//from important
int main()
{
    Base *b = new Base;//错误,无法实例化对象
}
title:Error2
collapse:open
```cpp
//from important
class Son :public Base;
 
int main()
{
    Son s;//错误,子类还未重写
}
title:Success
collapse:open
```cpp
class Son :public Base
{
public:
    virtual void func(){};//尽管没有写任何东西,但编译器依然认为已重写
};
 
class Son2 :public son
{
public:
    virtual void func()
        cout << "func()调用" << endl;
};
 
int main()
{
    Son s;
    Base *base = new Son2;
    
    base->func();
    //output:func()调用
}

虚析构和纯虚析构

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

title:Important
collapse:open
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
 
虚析构和纯虚析构区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
title:warning
collapse:open
```cpp
class Animal
{
public:
    Animal(string name){cout << "Animalgouzao" << endl;}
    virtual void speak() = 0;
    ~Animal(){cout << "Animalxigou" << endl;}
};
 
class Cat: public Animal
{
public:
    Cat(string name)
    {
        cout << "Catgouzao" << endl;
        m_name = new string(name);
    }
    
    virtual void speak()
        {cout << m_name << "miao" << endl;}
    
    ~Cat()
    {
        if(m_name != NULL)
        {
            cout << "xigou" << endl;
            delete m_name;
            m_name = NULL;
        }
    }
    
    string *m_name;
};
 
class Dog: public Animal
{
public:
    Dog(string name){cout << "Doggouzao" << endl;}
    ~Dog(){cout << "Dogxigou" << endl;}
    void speak()
        {cout << "wang" << endl;}
};
 
int main()
{
    Animal *animal = new Cat("Tom");
    animal->speak();
    delete animal;
    return 0;
}
/*output:
Animalgouzao
Catgouzao
Tommiao
Animalxigou
*/
//没有Cat子类析构
title:Question
collapse:open
父类指针在析构时,不会调用子类析构,导致子类如果有堆区属性,出现内存泄漏
 
解决方法:父类析构改为虚析构
title:Success
collapse:open
```cpp
//from warning
virtual ~Animal(){...}
 
//纯虚析构实现
//from warning
virtual ~Animal() = 0;
//outclass
Animal::~Animal()
{cout << "Animalxigou" << endl;}
title:Summary
collapse:open
如果要创建一个指向父类的子类对象指针,建议父类析构使用纯虚析构

学习路径