C++ : 多态性


多态:同一操作作用于不同的类的实例,将产生不同的执行结果,即一个接口,多种方法。即不同类的对象收到相同的消息时,得到不同的结果。在程序中消息就是调用函数,不同的行为即执行不同的函数体。

  1. 编译时的多态性 -> 静态多态性 -> 函数重载
  2. 运行时的多态性 -> 动态多态性 -> 虚函数

函数重载

同一类内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。用来处理实现功能类似数据类型不同的问题。

虚函数

在基类中声明为virtual的函数,并在一个或多个派生类中被重新定义的成员函数,运行时将会根据对象的实际类型来调用相应的函数。

作用:运行时多态,基类中提供虚函数的实现,为派生类提供默认的函数实现。派生类可以重写基类的虚函数实现派生类的特殊化。

1.虚函数可以通过基类指针或引用来访问基类和派生类中的同名函数
基类指针是用来指向基类的,如果用它来指向派生类对象,则进行指针类型转换,将派生类对象的指针先转换为基类的指针,所以基类指针指向的是派生类对象中的基类部分。虚函数的运用使得在派生类的基类部分中,派生类的虚函数取代了原来的虚函数。
2.同名函数area在基类中定义为虚函数,定义一个指向基类对象的指针变量pt。pt指向基类对象时,pt->area输出基类对象的数据成员;pt指向派生类对象A时,pt->display输出派生类A对象的数据成员;pt指向派生类对象B时,pt->display输出派生类B对象的数据成员。

#include <iostream>
using namespace std;
class Shape
{
    public:
        Shape(int a=0, int b=0){
            width = a; height = b;
        }
        virtual int area(){
            cout << "Parent class area :" <<endl;
            return 0;
        }
    protected:
        int width, height;
};
class Rectangle: public Shape
{
    public:
        Rectangle(int a=0, int b=0):Shape(a, b) { }
        int area(){
            cout << "Rectangle class area :" <<endl;
            return (width * height); 
        }
};
class Triangle: public Shape
{
    public:
        Triangle(int a=0, int b=0):Shape(a, b){ }
        int area(){
            cout << "Triangle class area :" <<endl;
            return (width * height / 2); 
        }
};

int main()
{
    Rectangle rec(10,7);
    Triangle tri(10,5);

    Shape *shape;
    shape = &rec;    // 存储矩形的地址
    shape->area();   // 调用矩形的求面积函数 area

    shape = &tri;    // 存储三角形的地址
    shape->area();   // 调用三角形的求面积函数 area
    return 0;
}

虚函数表

vtbl,每个类使用一个虚函数表,每个类对象用一个虚表指针指向虚函数表。类中有N个虚函数,那么其虚函数表将有N*4字节。

编译器处理虚函数

为每个类对象添加一个隐藏成员,来保存一个指向函数地址数组(虚函数表)的指针,称为虚表指针(vptr)
基类对象包含一个虚表指针,指向基类中虚函数表。派生类对象包含一个虚表指针,指向派生类虚函数表。
1.如果派生类重写基类的虚方法,派生类虚函数表将保存重写的虚函数地址,不是基类的虚函数地址;
2.如果基类中的虚方法没有在派生类中重写,派生类将继承基类中的虚方法,派生类中虚函数表将保存基类中未被重写的虚函数的地址;
3.如果派生类中定义了新的虚方法,则该虚函数的地址被添加到派生类虚函数表中;
在这里插入图片描述
使用虚函数的调用过程:
1.编译器知道pb是类B的指针,不知道它指向的具体对象类型是B的对象或D的对象。对于pb->bar编译器能够确定的是此处operator->的另一个参数是B::bar
2.B::bar和D::bar在各自虚函数表中的偏移位置是相等的。
无论pb指向哪种类型的对象,只要能够确定被调函数在虚函数表中的偏移值,待运行时能够确定具体类型,并能找到相应vptr就能找出真正应该调用的函数。
B::bar是一个虚函数指针, 它的ptr部分内容为9,则它在B的虚函数表中的偏移值为8(8+1=9)
当程序执行到pb->bar()时,已经能够判断pb指向的具体类型了:
pb指向B的对象时,得到B对象的vptr,加上偏移值8((char*)vptr + 8), 可以找到B::bar
pb指向D的对象时,得到D对象的vptr,加上偏移值8((char*)vptr + 8), 可以找到D::bar
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

春夏与冬

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

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

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

打赏作者

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

抵扣说明:

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

余额充值