类的三大特性---多态

43 篇文章 1 订阅

1 多态的概述

在生活中,也有各种各样的多态情景:比如,当班主任说,8:50分钟,同学们要进直播间,上课,同学们分别进入各班级的直播间。

多态意义:

C++中所谓的多态(Polymorphism)是指,是由继承而产生的相关的不同的类,其对象对同一个消息会作不同响应。

意义在于能增加程序的灵活性,可以减轻系统升级,维护,调用的工作量和复杂度,程序的可扩展性会大大增强。

比如:

在设计动物类时,会设计比如吃,睡觉等方法,对于现在的条件,只能领养狗

动物类:吃,睡觉

狗类

等五年后,发财了,又想领养动物,比如:猫

程序员只需要自定义一个猫的类,来继承于动物类,以前设计好的动物类,狗类都不需要来进行修改。

2 多态形成条件

  1. 首先要有继承,并且基类中有虚函数 ---- 基类中完成
  2. 派生类中要重写(复写---override)基类的虚函数 ---- 派生类完成
  3. 派生类对象的地址赋值给基类的指针,通过基类的指针,调用虚函数 (基类指针(*)指向派生类对象)或基类的引用(&)引用派生类对象 ----> 具体实际应用来完成

3 多态实现

#include <iostream>

using namespace std;


//设计一个基类,用来绘制形状
class Sharp
{
protected:
    int x;
    int y;
public:
    Sharp(int x=0,int y=0):x(x),y(y)
    {

    }

    //设计一个虚函数:关键字(virtual) + 普通成员函数
   virtual  void draw()
    {
        cout << " Sharp:" << "("<<x << "," << y << ")"<<endl;
    }
};

class Circle:public Sharp
{
public:
    Circle(int x,int y,int radius):Sharp(x,y)
    {
        this->radius = radius;
    }
    // override 在派生类中重写基类的虚函数,可以加virtual也可以不加,都不影响

   virtual  void draw()
    {
       cout << " Circle:" << "("<<x << "," << y << ")" << "radius = " << this->radius<<endl;
    }


private:
    int radius;
};

///
//在设计新的需求功能时,原来的功能模型和框架仍不需要进行修改
class Rect:public Sharp
{
public:
    Rect(int x=0,int y=0,int w = 0,int h=0):Sharp(x,y)
    {
        this->height = h;
        this->weight = w;
    }

    // override 在派生类中重写基类的虚函数,可以加virtual也可以不加,都不影响
   virtual  void draw()
    {
            cout << " Rect:" << "("<<x << "," << y << ")";
            cout << "weight = " << this->weight;
            cout << "height = " << this->height << endl;
    }
private:
    int weight;
    int height;
};



//在具体过程中,可以通过全局函数 或者 通过其他类(设计师类)
void draw_function(Sharp* pr) //很多年后,接口还是可以继续使用
{
    pr->draw();
}

void draw_function(Sharp& pr)
{
      pr.draw();
}

//关于多态的注意事项:
//1.基类中的虚函数重写对于派生类是可选的,如果不重写,那么该函数在被调用时,会调用基类中的虚函数
  //把这个过程叫作事件的传递给基类,由基类来处理事件,派生类是不知道基类处理了没有,它只起到一个传递的作用
  //如果派生类重写,那么该函数在被调用时,会调用派生类中的虚函数,那这个过程叫作事件的拦截,该事件不再会
  //传递给基类
int main()
{
    Circle c(3,4,5);

    //调用绘制形状函数
    draw_function(&c); //基类指针指向了派生类对象,那么在程序运行时,发生了一个多态现象
                       //执行了派生类中的 draw函数,而不是基类中的draw函数。
                       //当去掉基类中关键字(virtual)后,就不会调用派生类中的draw函数
                       //所以说,多态的实现就是通过virtual关键字来完成。
    draw_function(c);
    Rect r(1,2,8,9);
    draw_function(&r);
    draw_function(r);
    return 0;
}

4 重载 、重写(覆盖)和重定义

重载:

  1. 在同一个类中(相同范围)
  2. 函数名字相同
  3. 参数不同(参数个数 参数类型 参数顺序)
  4. virtual关键字不作为判断标准,可有可无

重写(覆盖):是指派生类函数重写基类的函数

  1. 不在同一个类中,分别位于基类和派生类中
  2. 参数相同
  3. 函数名相同
  4. 基类中函数 必须有virtual关键字,派生类可有可无

重定义(隐藏):是指派生类的函数屏蔽了与其同名的基类函数

  1. 如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无virtual,基类的函数被隐藏
  2. 如果派生类的函数 与基类的函数同名,并且参数也相同,但是,基类函数没有virtual关键字,此时,基类的函数被隐藏。
class Base
{

public:
    void func(int a = 0)
    {
        cout << "Base::func() " << endl;
    }
};

class Derived :public Base
{

public:
#if 1
    void func(int a = 0)
    {
        cout << "Derived::func() " << endl;
    }
#endif
};


int main()
{
    Derived d;
    d.func();

    Base* pd = &d;
    pd->func();
    /*
Derived::func()
Base::func() ---->基类中,没有虚函数,是不会发生多态,而派生类中和基类中同名函数,这种关系被叫作重定义
   */
    return 0;
}

总结:

  1. 在继承时,加virtual,可以实现虚继承
  2. 继承中,在基类中函数定义时,加virtual,可以实现多态
  3. 关键字同一个,但是意义不一样,实现方式不一样,虚继承时,在访问基类成员,进行拷贝操作,真正的成员存储只有一份;多态时,在访问虚函数时,进行动态绑定,但是都是使用virtual来实现,管理方式都是一样,虚继承,继承的派生类中,会分配一个vptr指针,在多态中,分配一个vptr指针 ,还有一个vptrtable

5 多态的原理实现

虚函数表(vtable)和vptr

  1. 当类中声明虚函数时,编译器会在类中生成一个虚函数表
  2. 虚函数表是一个存储成员函数指针的结构
  3. vtable和vptr都是由编译器自动生成和维护
  4. vitrual成员函数会被编译器放入到虚函数表中
  5. vptr指向vtalbe这个过程,也是由编译器来维护

总结:

在一个类中,成员函数:普通成员函数和虚函数

它们之间有所不同,在函数调用时,那么普通成员函数的调用效率要高于虚函数,所以,在进行类设计时,如果没有要实现多态,原则上不要把函数声明为虚函数,原因在于普通成员函数是直接调用(在编译时,根据类型来确定调用关系),而虚函数通过vptr指针,进行虚函数表中查找,重新进行寻址操作才能确定真正应该要调用的函数。

6 纯虚函数 和抽象类

在多继承和多态中,对于基类的虚函数 而言,通常情况下,在派生类中,都会进行重写,从面可以实现多态,那么,对于基类中虚函数 实现,在一些情况下,意义不大。从而在C++中,有了更好的解决方案,使用纯虚函数处理。

纯虚函数语法:使用0来给函数进行赋值,那么该函数就是纯虚函数

virtual 函数声明 = 0;

抽象类:一个类中,拥有纯虚函数 的类,它是一个抽象类,该类不能实例化.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值