多态的基本概念

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

1、多态分为两类

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

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

2、静态多态和动态多态区别

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

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

3、多态满足条件

①有继承关系

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

4、多态使用条件

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

5、重写

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

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.speak();
}

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

    Dog dog;
    doSpeak(dog);
}

void test02(){
    cout <<"sizeof(Animal):"<<sizeof(Animal)<<endl;
}
多态案例—计算器类

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

多态的优点:

①代码组织结构清晰

②可读性强

③利于前期和后期的扩展以及维护

//普通写法
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;
        }        
        //如果想扩展新的功能,需要修改源码
    }
    int m_Num1;
    int m_Num2;
};

void test01(){
    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 AbstructCalculator{
public:
    virtual int getResult(){
        return 0;
    }
    int m_Num1;
    int m_Num2;
};

class AddCalculator:public AbstructCalculator{
public:
    int getResult(){
        return m_Num1+m_Num2;
    }
};

class SubCalculator:public AbstructCalculator{
public:
    int getResult(){
        return m_Num1-m_Num2;
    }
};

class MulCalculator:public AbstructCalculator{
public:
    int getResult(){
        return m_Num1*m_Num2;
    }
};

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

    //加法运算
    AbstructCalculator *abc =new AddCalculator;
    abc->m_Num1 =10;
    abc->m_Num2 =10;
    cout<< abc->m Num1<<"+"<<abc->m Num2<<"=" <<abc->getResult()<< endl;
    //用完后记得销毁
    delete abc;
}
纯虚函数和抽象类

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

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

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

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

4、抽象类特点:

①无法实例化对象

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

class Base{
public:
    virtual void func()=0;
    
};

class Son:public Base{
public:

};

class Son1:public Base{
public:
    virtual void func(){};
};

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

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

    Base *base =new Son;
    base->func();
}

虚析构和纯虚析构

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

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

2、虚析构和纯虚析构共性

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

②都需要有具体的函数实现

3、虚析构和纯虚析构区别

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

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

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

6、总结

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

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

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

class Animal{
public:
    Animal(){
        cout<<"Animal构造函数" <<endl;
    }
    //利用虚析构可以解决父类指针释放子类对象时不干净的问题
    virtual ~Animal(){
        cout<<"Animal虚析构函数" <<endl;
    }
    //纯虚析构 需要声明也需要有实现 纯虚析构,该类属于抽象类,无法实例化对象
    virtual void speak()=0;
    
    virtual void speak()=0;
};
Animal::~Animal(){
   cout<<"Animal纯虚析构函数" <<endl; 
}

class Cat:public Animal{
public:
    Cat(String name){
        cout<<"小猫构造函数" <<endl;
        m_Name=new string(name);
    }
    ~Cat(){
        if(m_Name!=NULL){
            cout<<"小猫析构函数" <<endl;
            delete m_Name;
            m_Name=NULL:
        }
    };

     virtual void speak(){
         cout <<*m_Name<<"小猫在说话" <<endl;
     };  
    string *m_Name;
};

void test01(){
    Animal* animal =new Cat("Tom");
    animal->speak();
    //父亲指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
    delect animal;
};
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐