【C++】类和对象-多态

目录

一、多态的基本概念

        1、静态多态

        2、动态多态

二、多态的原理剖析

三、多态案例 —— 计算器类

四、纯虚函数和抽象类

五、虚析构和纯虚析构

        1、虚析构

        2、纯虚析构

六、多态案例 —— 组装电脑


一、多态的基本概念

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

-

两类多态

  • 静态多态:函数重载和运算符重载(复用函数名)
  • 动态多态:派生类和虚函数实现运行时多态

-

静态和动态的多态区别:

  • 静态多态的函数地址早绑定编译阶段确定函数地址
  • 动态多态的函数地址晚绑定运行阶段确定函数地址

1、静态多态

测试代码:

#include<iostream>
using namespace std;

class Person1
{
public:
    void print(){
        cout << "Person1" << endl;
    }
};

class Person2 :public Person1
{
public:
    void print(){
        cout << "Person2" << endl;
    }
};

void myPrint(Person1& p1){
    p1.print();
}

int main(){
    Person2 p2;
    myPrint(p2); // 父类引用接受子类对象
    system("pause");
    return 0;
}

运行结果:

 地址早绑定,编译阶段就确定了地址,无论传入的是子类的什么引用,调用的都是父类的函数。

-

想要调用子类的函数就要地址晚绑定(动态多态)

2、动态多态

在父类的函数前加关键字 virtual 让函数成为虚函数

测试代码:

#include<iostream>
using namespace std;

class Person1 {
public:
    // 虚函数
    virtual void print() {
        cout << "Person1" << endl;
    }
};

class Person2 :public Person1 {
public:
    void print() {
        cout << "Person2" << endl;
    }
};

void myPrint(Person1& p1) {
    p1.print();
}

int main() {
    Person2 p2;
    myPrint(p2);
    system("pause");
    return 0;
}

 运行结果:

根据传入对象执行类的函数,print() 函数不能提早确定。

-

动态多态满足条件

1、有继承关系

2、子类重写父类虚函数

(重写需要函数完全一致,参数列表,返回值,函数名,子类 virtual 可以写也可以不写)


二、多态的原理剖析

 计算类中含有虚函数所占的空间大小

64bit 指针大小 8 字节

vs 开发者命令提示符工具,当子类继承父类虚函数


三、多态案例 —— 计算器类

描述:
利用多态技术,设计实现两个操作数进行运算的计算器类

-

多态的优点

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

-

实际开发中提倡 开闭原则 

开闭原则:对拓展进行开放,对修改进行关闭

测试代码:

#include<iostream>
using namespace std;

// 实现计算器抽象类
class AbstractCalculator {
public:
    virtual int getResult(){
        return 0;
    }
    int m_Num1;
    int m_Num2;
};
// 乘法类
class MulCalculator :public AbstractCalculator{
public:
    virtual int getResult(){
        return m_Num1 * m_Num2;
    }
};
// 加法类
class AddCalculator :public AbstractCalculator{
public:
    virtual int getResult(){
        return m_Num1 + m_Num2;
    }
};
//
// 可继续添加

void test(){
    AbstractCalculator* abc = new AddCalculator;
    abc->m_Num1 = 10;
    abc->m_Num2 = 10;
    
    cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
}
int main(){
    test();
    system("pause");
    return 0;
}

运行结果:


四、纯虚函数和抽象类

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

-

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

-

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

-

当类中有纯虚函数(父类),这个类称为 抽象类

-

抽象类特点:

  • 无法实例化对象

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

测试代码:

#include<iostream>
using namespace std;

// 抽象类
class Base{
public:
    // 纯虚函数
    virtual void func() = 0;
};

class Son1 :public Base{
public:
    // 重写父类虚函数
    virtual void func()
    {
        cout << "Son1::func 函数调用" << endl;
    }
};

class Son2 :public Base
{
public:
    // 重写父类虚函数
    virtual void func()
    {
        cout << "Son2::func 函数调用" << endl;
    }
};

void test()
{
    // 通过父类指针调用不同子类的函数
    Base* base1 = new Son1;
    base1->func();
    Base* base2 = new Son2;
    base2->func();
}

int main(){
    test();
    system("pause");
    return 0;
}

运行结果:


五、虚析构和纯虚析构

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

-

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

-

虚析构和纯虚析构共性:

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

虚析构和纯虚析构区别:

  • 类中有纯虚析构,该类属于抽象类无法实例化

-

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

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

问题:父类指针在析构时候不会调用子类中的析构函数,导致子类如果有堆区属性,就会出现内存泄漏

1、虚析构

测试代码:

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

class Person1 {
public:
    // 构造
    Person1() {
        cout << "父类 构造函数调用" << endl;
    }
    // 纯虚函数
    virtual void Print() = 0;
    // 析构
    ~Person1() {
        cout << "父类 析构函数调用" << endl;
    }
};

class Person2 :public Person1 {
public:
    // 构造
    Person2(string name) {
        cout << "子类 构造函数调用" << endl;
        m_Name = new string(name);
    }
    // 重写
    virtual void Print() {
        cout << *m_Name << " 子类 中 Print" << endl;
    }
    // 析构函数释放堆区数据
    ~Person2() {
        if (m_Name != NULL) {
            cout << "子类 析构函数调用" << endl;
            delete m_Name;
            m_Name = NULL;
        }
    }
    string* m_Name;
};

void test() {
    // 父类指针指向子类对象,开辟堆区数据
    Person1* p1 = new Person2("lihua");
    p1->Print();
    // 释放堆区数据
    delete p1;
}

int main(){
    test();
    system("pause");
    return 0;
}

运行结果:

 修改:将父类的析构变成虚析构

2、纯虚析构

virtual ~Person1() = 0;

如果纯虚析构只有声明会产生一个无法解析外部命令的错误(链接阶段的错误)

需要代码实现

class Person1 {
public:
    // 纯虚析构
    virtual ~Person1() = 0;
};

// 纯虚析构带代码实现
Person1::~Person1() {
    cout << "Person1 下的纯虚析构函数" << endl;
}

 类中含有纯虚函数这个类变为抽象类,无法实例化


六、多态案例 —— 组装电脑

代码:

#include<iostream>
using namespace std;

// cpu 抽象类
class CPU{
public:
    // cpu计算纯虚函数
    virtual void calculator() = 0;
};

// gpu抽象类
class GPU{
public:
    // gpu显示纯虚函数
    virtual void display() = 0;
};

// 内存抽象类
class MEM{
public:
    // memory存储纯虚函数
    virtual void sterage() = 0;
};

// 继承 CPU 的子类,重写父类纯虚函数
class Intel_CPU :public CPU{
public:
    // 重写
    virtual void calculator(){
        cout << "Inter_CPU" << endl;
    }
};

// 继承 GPU 的子类,重写父类纯虚函数
class Intel_GPU :public GPU{
public:
    // 重写
    virtual void display(){
        cout << "Inter_GPU" << endl;
    }
};

// 继承 MEM 的子类,重写父类纯虚函数
class Intel_MEM :public MEM{
public:
    // 重写
    virtual void sterage(){
        cout << "Inter_MEM" << endl;
    }
};

class AMD_CPU :public CPU{
public:
    // 重写
    virtual void calculator(){
        cout << "AMD_CPU" << endl;
    }
};

class AMD_GPU :public GPU{
public:
    // 重写
    virtual void dispaly(){
        cout << "AMD_GPU" << endl;
    }
};

class AMD_MEM :public MEM
{
public:
    // 重写
    virtual void sterage(){
        cout << "AMD_MEM" << endl;
    }
};

// computer类
class Computer
{
public:
    // 构造
    Computer(CPU* cpu, GPU* gpu, MEM* mem){
        m_cpu = cpu;
        m_gpu = gpu;
        m_mem = mem;
    }
    // 拼装函数
    void work(){
        m_cpu->calculator();
        m_gpu->display();
        m_mem->sterage();
    }
    // 析构
    ~Computer(){
        if (m_cpu != NULL) {
            delete m_cpu;
            m_cpu = NULL;
        }
        if (m_gpu != NULL) {
            delete m_gpu;
            m_gpu = NULL;
        }
        if (m_mem != NULL) {
            delete m_mem;
            m_mem = NULL;
        }
    }
private:
    CPU* m_cpu;
    GPU* m_gpu;
    MEM* m_mem;
};

void test(){
    // 堆区创建配件
    CPU* IntelCPU = new Intel_CPU;
    GPU* IntelGPU = new Intel_GPU;
    MEM* AMDMEM   = new AMD_MEM;
    // 实例化 Computer类 对象,传入指针
    Computer* computer = new Computer(IntelCPU, IntelGPU, AMDMEM);
    // 调用对象成员函数 - 拼装函数
    computer->work();
    // 销毁堆区数据
    delete computer;
}

int main()
{
    test();

    system("pause");
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值