C++多态

静态多态:函数重载(运算符重载),模板属于静态多态,在编译期间就能确定的多态。

动态多态:父类的指针或引用指向子类对象,通过指针或引用调用子类的虚函数,在运行期间才能确定具体调用哪个函数,是动态多态

重写:只有虚函数才可以被重写,子类重写父类虚函数的条件是:返回值 函数名参数相同,并且是虚函数

启动多态的条件:有继承关系,子类重写父类虚函数并且父类指针调用子类重写的虚函数。

#include<iostream>
#include<vector>
#include<string>
using namespace std;

class A
{
public:
    int a;
    int b;
    int c;
    const int d = 1;
    virtual void work()
    {
        cout << "A work" << endl;
    }
    void fun()
    {
        cout << "A fun" << endl;
    }
};

class B: public A
{
public:
    virtual void work()
    {
        cout << "重写了父类work函数" << endl;
    }
    void fun()
    {
        cout << "B fun" << endl;
    }
};

class C: public B
{
public:
    virtual void work()
    {
        cout << "重写了父类的work函数" << endl;
    }
    void fun()
    {
        cout << "C fun" << endl;
    }
};

int main()
{
    //多态父类指针指向子类对象
    //父类指针指向子类对象,去调用虚函数的时候会调用被重写的虚函数
    A* a = new C;
    a->fun();//函数隐藏:只需要函数名字相同,会调用指针类型的函数
    a->work();
}

C++多态是如何实现的

为了实现C++的多态,C++使用了一种动态绑定技术。这个技术的核心是虚函数表。下表面介绍虚函数是如何实现动态绑定的

类的虚表

1、每个包含了虚函数的类都包含一个虚表

2、当一个类(A)继承另一个类(B)时,类A会继承类B的函数调用权。所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。

3、以下的代码类A包含虚函数Vfunc1,vfunc2,由于类A包含虚函数,故类A拥有一个虚表指针。

class A
{
public:
    virtual void vfunc1();
    virtual void Vfunc2();
    void func1();
    void func2();
private:
    int m_data1, m_data2;
};

 

# 虚表是一个指针数组,其内的元素是虚函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是:普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。

# 虚表内的条目,即系函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了

虚表指针

# 虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都是用同一个虚表。

# 为了指定对象的虚表,对象内部包含了一个虚表的指针,来指向自己所使用的虚表,为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针,*_vptr,来指向虚表。这样,当类的对象在创建的时候便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。 

上表指出,一个继承类的基类如果包含虚函数,那这个继承类也拥有自己的虚表,故这个继承类的对象也包含一个虚表指针,用来指向它的虚表

class A
{
public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();
private:
    int m_data1, m_data2;
};

class B: public A
{
public:
    virtual void vfunc1();
    void func1();
private:
    int m_data3;
};

class C: public B
{
public:
    virtual void vfunc2();
    void func2();
private:
    int m_data1, m_data4;
};

 

# 由于这三个类都有虚函数,故编译器为每个类都创建了一个虚表,即类A的虚表(A vtbl),类B的虚表(B vtbl),类C的虚表(C vtbl)。类A,类B,类C,的对象都拥有一个虚表指针,*_vptr用来指向自己所属类的虚表。

# 类A包括两个虚函数,故A vtbl包含了两个指针,分别指向A::vfunc1()和A::vfunc2()。

# 类B继承于类A,故类B可以调用A的函数,但由于类B重写了B::vfunc1()函数,故Bvtbl的两个指针分别指向B::vfunc1()和A::vfunc2()。

# 类C继承于类B,故类C可以调用类B的函数,但由于类C重写了C::vfunc2()函数,故Cvtbl的两个指针分别指向B::vfunc1()(指向继承的最近的一个类的函数)和C::vfunc2()。

##对象的虚表指针用来指向自己所属类的虚表,需表中的指针会指向其继承的最近的一个类的虚函数。

# 非虚函数的调用不需要经过虚表,故不需要虚表中的指针指向这些函数

# 假设我们定义一个类B的对象。由于bObject是类B的uige对象,故bObject包含一个虚表指针,指向类B的虚表。

int main()
{
    B bObject;
}

我们声明一个类A的指针P来指向对象bObject。虽然p是基类的指针只能指向基类的部分,但是虚表指针也属于基类部分,所以p可以访问到对象bObject的虚表指针。bObject的虚表指针指向类B的虚表,所以P可以访问到Bvtbl。

int main()
{
    B bObject;
    A *p = & bObject;//父类指针指向子类对象
}

当我们使用p来调用vfunc1()函数时,会发生什么现象?

int main()
{
    B bObject;
    A *p = & bObject;
    p->vfunc1();
}

程序在执行p->vfunc1()时,会发现p是个指针,且调用的函数是虚函数,接下来便会进行以下的步骤。

#  首先,根据虚表指针p->_vptr来访问对象bObject对应的虚表。虽然指针p是基类A*类型,但是*_vptr也是基类的一部分,所以可以通过p->vptr可以访问到对象对应的虚表。

#  然后,在虚表中查找所调用的函数对应的条目。由于虚表在编译阶段就可以构造出来了,所以可以根据所调用的函数定位到虚表中的对应条目。对于p->vfunc1()的调用,Bvtbl的第一项即是vfunc1对应的条目。

#  最后根据虚表总找到的函数指针,调用函数。从图中可以看到,Bvtbl的第一项指向B::vfunc1(),所以p->vfunc1()实质会调用B::vfunc1()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿瑞不会C++

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

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

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

打赏作者

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

抵扣说明:

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

余额充值