C++为何引入多态及多态的实现

#include <iostream>

using namespace std;
class Animal{
public:
     void sleep(void)
    {
        cout << "Animal::sleep() - 动物在睡觉" << endl;
    }
};

class Cat:public Animal{
public:
    void sleep(void)
    {
        cout << "Cat::sleep() - 猫在睡觉" << endl;
    }
};

int main(int argc, char *argv[])
{
    Animal *p1 = new Cat;
    //Animal::sleep() - 动物在睡觉
    p1->sleep();

    //error: invalid conversion from 'Animal*' to 'Cat*' [-fpermissive]
    Cat *p2 = new Animal;
    return 0;
}

使用基类指针指向子类对象是安全的
p1指向Cat类的实例对象时,访问的内存范围不会超出Animal类的大小,因此也不会超出Cat类实例对象的大小,即不会访问非法空间。
但是使用p1调用sleep()成员方法时,p1Animal类的指针,会去代码区寻找Animal类中的方法sleep(),而我们想要的调用的是Cat类中的sleep()方法

使用子类指针指向基类对象是不安全的
当p2指向Animal的实例对象时,因为子类的内存大小一定是大于等于父类的,因此p2可能访问到Animal的实例对象后面的内存数据,而访问非法空间是不允许的。为了安全起见,编译器会报错,禁止子类指针指向父类实例化对象。

为了使用基类指针访问子类对象中的成员方法,C++引入了虚函数
Animal类修改如下:

class Animal
{
public:
    //虚函数 
    virtual void sleep(void)
    {
        cout<< "Animal::sleep() - 动物在睡觉" <<endl;
    }
};

当成员函数用virtual修饰成为虚函数时,实例化对象中将产生一个虚基类指针,该指针指向一个虚基类表,该表中存放着类中的虚函数和虚函数对应的函数入口地址。当通过基类指针访问子类中的virtual成员函数时,会去调用虚基类表中的对应函数。
Animal类的内存结构Cat类的内存结构为何要使用基类指针指向子类对象
当函数的形参为基类对象时,传入其子类对象为实参,可以实现一个函数对不同对象的实现多种操作。避免代码的冗杂

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void sleep(void)
    {
        cout<<"父亲在睡觉"<<endl;
    }
};

class Son1:public Base
{
public:
    void sleep(void)
    {
        cout<<"Son1在安静的睡觉"<<endl;
    }
};

class Son2:public Base
{
public:
    virtual void sleep(void)
    {
        cout<<"Son2在轻度的睡觉"<<endl;
    }
};

class Son3:public Base
{
public:
    virtual void sleep(void)
    {
        cout<<"Son3在雨声般的睡觉"<<endl;
    }
};

class Son4:public Base
{
public:
    virtual void sleep(void)
    {
        cout<<"Son4在鼾声如雷"<<endl;
    }
};


//以基类指针作为函数的参数 函数可以操作该基类派生出的任意子类对象
void sleepFun(Base &ob)
{
    ob.sleep();
}

int main(int argc, char *argv[])
{
    Son1 ob1;
    Son2 ob2;
    Son3 ob3;
    Son4 ob4;
    
    sleepFun(ob1);
    sleepFun(ob2);
    sleepFun(ob3);
    sleepFun(ob4);
    
    return 0;
}

运行结果:
在这里插入图片描述使用基类指针指向子类对象时,程序结束只能调用父类的析构函数
使用前面的Animal类和Cat类,执行下方代码

void test01()
{
    //通过基类  指针、引用 访问子类的成员函数
    Animal *p = new Cat;
    
	//调用的子类的sleep
    p->sleep();

    //出现的问题:只能释放 父类析构
    delete p;
}

使用new创建Cat示例对象,先调用Animal的构造函数,再调用Cat类的构造函数。但是使用new动态创建的对象不会自动释放。delete p;时,根据p的类型去调用了Animal类的析构函数,而没有调用子类的析构函数。

将父类的析构函数用virtual修饰,形成虚析构

    virtual ~Animal()
    {
        cout<<"animal析构"<<endl;
    }

原理分析:
编译器识别虚析构,将析构函数放入虚基类表,delete基类指针时,将根据子类中维护的虚基类表调用子类的析构函数,调用完子类的析构函数,编译器将自动调用其父类的析构函数。
原理图:

在这里插入图片描述纯虚函数和抽象类
使用基类指针指向子类对象时,通过虚函数调用的实际成员方法是子类自己的方法。那么基类中的方法根本得不到调用,为何不直接不去实现基类的成员函数呢?
为此,C++引入纯虚函数,使基类中的成员函数不需要实现。代码如下:

virtual void sleep(void) = 0;

如果基类中存在纯虚函数,也就意味着代码区的父类方法根本没有得到定义,如果使用该父类实例化对象,也根本找不到成员方法。因此,C++规定一旦类中存在纯虚函数,该类即为抽象类,抽象类不能实例化对象

纯析构函数
和纯虚函数不一样的是,基类中的纯析构函数需要在类外定义,因为子类析构完成后,会自动调用父类的析构函数
代码示例如下:

class Base
{
public:
    virtual ~Base()=0;
};
Base::~Base()
{函数体}

总结:

虚函数:virtual修饰函数体(作用于成员函数)
目的:通过基类指针或引用操作子类的方法

class Base
{
public:
    virtual my_fun(void)
    {
        //有函数体;
    }
}

纯虚函数:virtual修饰,参数列表后加=0,没有函数体,所在的类为抽象类
目的:为子类提供固定的流程和接口

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

虚析构:virtual修饰类中的析构函数
目的:为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象

class Base
{
public:
    virtual ~Base()
    {函数体}
}

纯虚析构:virtual修饰函数,参数列表后加=0, 必须实现析构的函数体
目的:用基类的指针删除派生类对象、同时提供固定接口

class Base
{
public:
    virtual ~Base()=0;
}
Base::~Base()
{函数体}

补充:

重载:
同一作用域的同名函数、参数个数,参数顺序,参数类型不同
和函数返回值,没有关系
const也可以作为重载条件

int fun(int a){}

int fun(int b,int c){}

int fun(char b,int c){}

重定义(隐藏)
子类继承父类后,重新定义父类(基类)的同名成员(非virtual函数)

class Base{
public:
    void fun(int){}
    void fun(int,int){}        
}
class Son:public Base{
public:
    void fun(参数可以不同){}//重定义
}

重写(覆盖)
子类重写父类的virtual函数,函数返回值,函数名字,函数参数,必须和基类中的虚函数一致

class Base{
public:
    virtual void fun(int){}
}
class Son:public Base{
public:
    virtual void fun(int){}//重写
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值