C++多态的原理

面向对象的三个基本特征是:封装、继承、多态

今天简单说一下C++多态的原理:

首先我们先看一个代码:

class A
{
    public:
    virtual void foo()
    {
        cout<<"我是父类"<<endl;    
    }
}
class B :public A
{
    public:
   virtual void foo()
        {
            cout<<"我是子类"<<endl;
        }
}
int main(int argc,char**argv)
{
    A *p = new B;
    p->foo();
    return ;
}

类A 中有虚函数,作为虚基类,暂且把A叫做父类,B叫做子类

这个代码main函数中new了一个子类对象B,用父类A类型的指针p指向了子类B对象,p->foo(),这句代码执行的结果不是调用父类中的foo函数,而是调用子类的foo函数

运行结果是: 我是子类

父类中有虚函数,并且子类继承了父类,子类重写了父类的虚函数:

这份代码我们先来看一下编译器做了哪些事情:

对于A类,编译器看到有一个虚函数,在类A中会加上一个指针vfptr(虚函数指针),并且会创建一个表vftable(虚函数表,实质是一个数组,每个元素用来保存函数的地址),

在 这个表中将父类中foo函数的地址放入虚函数表中,刚开始vfptr是没有指向虚函数表的,注意vfptr属于A类对象成员,而vftable不属于A类对象成员;

对于B类,继承了类A,也就继承了类A的vfptr成员,并且编译器也会给B类创建一个虚函数表,这个虚函数表保存了B类中foo函数的地址这个时候vfptr还没有指向vftable;

在main函数中,p->foo()这句代码,编译器会并不会将foo的地址直接绑定上,而是通过vfptr去调用foo函数:

p是一个指针,指向一个对象的地址,我们拿32位编译器来讲,想要得到foo函数的地址,其实很简单, *(int*)p就得到vfptr指针的值,这个值当创建对象之后会执行虚函数表,虚函数表的

第一个成语是foo函数的地址, 我们将foo函数的地址得到,(*(int*)(*(int*)p))得到的是foo函数的地址,调用这个函数(void (*)()) (*(int*)(*(int*)p)) ();

所以当我们的写上p->foo();这句代码的时候,起始编译器是幕后是调用了(void (*)()) (*(int*)(*(int*)p)) ()这个代码

p->foo()等价于(void (*)()) (*(int*)(*(int*)p)) ()

好了,以上就是我们编译器给我们幕后做的工作:

当我们运行程序时,new B,这句代码先是会调用父类A的构造函数,在构造函数中vfptr这个指针会被初始化指向类A中的vftable,然后父类A的构造函数调用完之后,

会调用子类B的构造函数,在B中会将vfptr这个指针指向子类B中的vftable,这样就很清晰了,下面的p->foo(),底层是(void (*)()) (*(int*)(*(int*)p)) ()调用形式,现在的vfptr

指向的是子类B中的虚函数表,自然调用的子类B中的foo函数.

是不是挺简单的,哈哈!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值