C/C++—— C++编译器是如何实现多态

关于C++中多态的基础知识参考博客:
C/C++—— 对多态现象的理解

C++中多态的实现原理

1.当类中声明虚函数时,编译器会在类中生成一个虚函数。
2.虚函数表是一个存储类成员函数指针的数据结构。
3.虚函数表是由编译器自动生成与维护的。
4.virtual成员函数会被编译器放入虚函数表中。
5.存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)。

C/C++—— C++编译器是如何实现多态_第1张图片

说明1:
通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。
说明2:
出于效率考虑,没有必要将所有成员函数都声明为虚函数

证明vptr指针的存在

定义一个没有虚函数类,查看该类的大小。然后再将其中的函数声明为虚函数,再查看该类的大小。结果发现该类大小增大了,可以知道增加了一个指针变量,该变量就是vptr指针。

没有虚函数:

#include <iostream>
using namespace std;

class Parent{
    public:
        Parent(int a = 0){
            this->a = a;
        }
        //virtual void print()
        void print()
        {
            cout << "Parent, a = " << a << endl;
        }
    private:
        int a;
};


int main()
{
    cout << "没有虚函数: ";
    cout << "sizeof(Parent) = " << sizeof(Parent) << endl;

    return 0;
}

输出为:
没有虚函数: sizeof(Parent) = 4

有虚函数:

#include <iostream>
using namespace std;

class Parent{
    public:
        Parent(int a = 0){
            this->a = a;
        }
        virtual void print()
        {
            cout << "Parent, a = " << a << endl;
        }
    private:
        int a;
};

int main()
{
    cout << "有虚函数: ";
    cout << "sizeof(Parent) = " << sizeof(Parent) << endl;
    return 0;
}

输出为:
有虚函数: sizeof(Parent) = 16
这里要解释一下为什么变为16了。。因为我电脑是64的linux系统,指针变量大小为8字节,又因为字节对齐的原因总的大小变为了16字节。

可以去参考博客:
C/C++——基本数据类型的大小并且sizeof(int *) = 8
C/C++—— 内存字节对齐规则

对象中的VPTR指针什么时候被初始化?

1.对象在创建的时,由编译器对VPTR指针进行初始化
2.只有当对象的构造完全结束后VPTR的指向才最终确定
3.父类对象的VPTR指向父类虚函数表
4.子类对象的VPTR指向子类虚函数表

当定义一个父类对象的时候比较简单,因为父类对象的VPTR指针直接指向父类虚函数表。
但是当定义一个子类对象的时候就比较麻烦了,因为构造子类对象的时候会首先调用父类的构造函数然后再调用子类的构造函数。当调用父类的构造函数的时候,此时会创建Vptr指针(也可以认为Vptr指针是属于父类的成员,所以在子类中重写虚函数的时候virtual关键字可以省略,因为编译器会识别父类有虚函数,然后就会生成Vptr指针变量),该指针会指向父类的虚函数表;然后再调用子类的构造函数,此时Vptr又被赋值指向子类的虚函数表。

上面的过程是Vptr指针初始化的过程。
这是因为这个原因,在构造函数中调用虚函数不能实现多态。
见下一篇博客:在构造函数中调用虚函数能实现多态吗

你可能感兴趣的:(C++-多态-虚函数)