C++:多态

本文详细介绍了C++中的多态原理,包括静态多态与动态多态的区别,以及静态联编和动态联编的工作机制。通过实例展示了如何使用虚函数实现动态绑定,以及在计算器类设计中如何利用多态实现开闭原则。同时,讨论了虚析构函数和纯虚析构函数的重要性,阐述了它们在内存管理和抽象类定义中的作用。
摘要由CSDN通过智能技术生成

一.多态原理

静态多态:函数重载
动态多态:函数重写 virtual关键字
静态联编:地址早绑定,编译阶段绑定好地址
动态联编:在运行阶段确定函数地址
多态:父类的引用或者指针指向子类对象
原理:对于一个只有成员函数的类 其大小为1 如果给成员函数添加virtual关键字,则大小变为4,这是由于该类内部出现一个指针 vfptr 虚函数表指针,指向虚函数表中储存的指向该类内的成员函数的地址,如果该子类没有声明与父类virtual函数相同的同名函数,或者声明了与父类中没加virtual的同名函数,构造函数中,子类的vfptr会拥有与父类的虚函数表相同的虚函数表 指向&Animal::speak,有子类继承该父类时,如果使用与父类中virtual声明的同名函数,叫做重写,重写必须返回值 参数 类型 顺序都相同 ,子类会将自己虚函数表中的父类函数地址改为自己的函数地址,vfptr会指向子类自己的虚函数表中该函数的地址 &Cat::speak()

Animal *animal = new Cat;
animal.speak(); 父类指针指向子类对象 调用子类重写后的函数

class Animal
{

public:
    virtual void speak()
    {
        cout<<"Animal Speak"<<endl;
    }
    virtual void eat()
    {
        cout<<"Animal eat"<<endl;
    }
};
class Cat:public Animal
{
public:
    void speak()
    {
        cout<<"Cat Speak"<<endl;
    }
    //子类中是否加virtual没影响
    virtual void eat()
    {
        cout<<"Cat eat"<<endl;
    }
};
//如果存在继承关系,编译器允许进行类型转换
void doSpeak(Animal& animal)
{
    animal.speak();
}

static void test01()
{
    Cat cat;
//   doSpeak(cat); //打印结果 Animal Speak  原因 调用doSpeak时 绑定好了Animal中speak函数的地址 属于早绑定,进行静态联编,编译阶段就确定好了地址

   //如果想调用Cat的speak,不能提前绑定好函数的地址,需要在运行时再去确定函数地址 即动态联编,在父类的成员函数前加virtual关键字,即虚函数
   //添加virtual后效果
    doSpeak(cat);  //打印 Cat Speak
    //静态多态:函数重载
    //动态多态:函数重载
    //静态联编:地址早绑定,编译阶段绑定好地址
    //动态联编:在运行阶段确定函数地址
    //多态:父类的引用或者指针指向子类对象
}

二.多态案例:计算器

真正的开发中,有个原则:开闭原则。
对扩展开放,对修改关闭。
使用多态 利于后期扩展 结构性好 可读性高 但是效率会变低 结构变复杂。
利用多态 在需要加入新的功能时,只需要添加相应的类即可。

普通虚函数:类内声明,类内实现,子类可以选择实现或者不实现。

纯虚函数:只声明,不实现,子类必须实现

如果父类有纯虚函数 子类中必须实现纯虚函数,否则该子类也是个抽象类(不能实例化对象)
如果父类有纯虚函数,该父类不能实例化对象
这个类有了纯虚函数,通常称为抽象类。

class abstractCalculator
{
public:
    void setval1(int a)
    {
        this->val1 = a;
    }
   void setval2(int b)
    {
        this->val2 = b;
    }
//   //虚函数
//    virtual int getResult()
//    {
//        return 0;
//    }
   //如果父类有纯虚函数 子类中必须实现纯虚函数,否则该子类也是个抽象类(不能实例化对象)
   //如果父类有纯虚函数,该父类不能实例化对象
   //这个类有了纯虚函数,通常称为抽象类。
   virtual int getResult() = 0;
    int val1;
    int val2;
};
class PulsCalculator:public abstractCalculator
{
public:
    virtual int getResult()
    {
        return val1 + val2;
    }
};
class SubCalculator:public abstractCalculator
{
public:
    virtual int getResult()
    {
        return val1 - val2;
    }
};

class subclass :public abstractCalculator
{
public:
    int getResult()
    {
        return 0;
    }

};
void test003()
{
    abstractCalculator* puls = new PulsCalculator;
    puls->setval1(10);
    puls->setval2(20);
    cout<<puls->getResult()<<endl;
    delete puls;
    puls = new SubCalculator;
    puls->setval1(10);
    puls->setval2(20);
   cout<<puls->getResult()<<endl;

   subclass subc;  //如果该子类不实现父类的纯虚函数 则报错 所以必须在子类中做实现

   //报错 含有纯虚函数的类不能实例化对象
//   abstractCalculator aaa;
//   abstractCalculator *aa= new abstractCalculator;
}

三.虚析构和纯虚析构

1.虚析构

普通析构是不会调用子类的析构的,所以在使用父类指针指向子类对象时,释放时,可能导致子类释放不干净。

class Animal1
{
public:
    virtual void speak()
    {
        cout<<"animal speak"<<endl;
    }
    //普通析构是不会调用子类的析构的,所以在使用父类指针指向子类对象时,可能导致子类释放不干净
    //利用虚析构解决 在虚构函数前加virtual
     virtual ~Animal1()
    {
        cout<<"Animal1 xigou"<<endl;
    }
};
class Cat1:public Animal1
{
public:
    Cat1(const char* name)
    {
        this->m_Name = new char[strlen(name)+1];
        strcpy(this->m_Name,name);
    }
    ~Cat1()
    {
        cout<<"Cat1 xigou"<<endl;
        if(this->m_Name != NULL)
        {
            delete[] m_Name;
            this->m_Name = NULL;

        }

    }
    virtual void speak()
    {
        cout<<"cat speak"<<endl;
    }
    char* m_Name;
};

void test004()
{
    Animal1* animal = new Cat1("TOM");
    animal->speak();
    //查看调用了哪个析构
    delete animal;   //打印:  Animal1 xigou
    //父类不使用虚析构时,调用了Animal1类的析构函数,不会调用子类的析构函数,和普通函数相同,所以需要父类使用虚析构函数
}

2.纯虚析构

纯虚析构需要类内声明,类外实现
出现纯虚析构函数,该类也是抽象类,不可实例化
子类虚构函数必须实现,否则也是抽象类
使用父类指针指向子类对象时,如果父类使用纯虚析构函数,则在释放时,先析构子类,再析构父类。

//纯虚析构
class Animal2
{
public:
    virtual void speak()
    {
        cout<<"animal speak"<<endl;
    }
    //纯虚析构
    //纯虚析构需要类内声明,类外实现
    //出现纯虚析构函数,该类也是抽象类,不可实例化
    
    //类内声明纯虚析构
     virtual ~Animal2() = 0;

};
//纯虚析构的类外实现
Animal2::~Animal2()
{
    cout<<"Animal2 Pure virtual destructor"<<endl;
    //Animal2的纯虚析构调用
}
class Cat2:public Animal2
{
public:
    Cat2(const char* name)
    {
        this->m_Name = new char[strlen(name)+1];
        strcpy(this->m_Name,name);
    }
    ~Cat2()
    {
        cout<<"Cat1 xigou"<<endl;
        if(this->m_Name != NULL)
        {
            delete[] m_Name;
            this->m_Name = NULL;
        }

    }
    virtual void speak()
    {
        cout<<"cat speak"<<endl;
    }
    char* m_Name;
};

void test005()
{
//    //报错 出现纯虚析构函数,该类也是抽象类,不可实例化
//    Animal2 an;
//    Animal2 *ann;


    Animal2* animal = new Cat2("TOM");
    animal->speak();
    //查看调用了哪个析构
    delete animal;  // 打印:Cat1 xigou   Animal2 Pure virtual destructor
    //先析构子类,再析构父类
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Michael.Scofield

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

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

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

打赏作者

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

抵扣说明:

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

余额充值