C++多态的原理

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

C++多态的原理_第1张图片

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

首先我们先看一个代码:

class A
{
    public:
    virtual void foo()
    {
        cout<<"我是父类"<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类对象成员;

C++多态的原理_第2张图片

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

C++多态的原理_第3张图片

在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函数.

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

你可能感兴趣的:(C语言)