以这段代码为例。
#include <iostream>using namespace std;class Parent
{
public:Parent(){}virtual void func1() {};virtual void func2() {};
};class Child :public Parent
{
public:Child():n(0),Parent(){cout << "Child()" << endl;}virtual void func1() {};void func2() {};void func3() {};private:int n;
};int main()
{Child c;return 0;
}
1. 虚函数表
虚函数表在编译时创建
编译时,对于包含虚函数的类,编译器会自动先为各个类创建好它们各自的虚函数表,其中对于子类重写父类的虚函数等工作也都完成了。
类的函数(包括虚函数)存储在代码段;
类的虚函数表存储在数据区中。
2. 虚函数表指针_vftptr
虚函数表指针在运行时创建
实例化含虚函数的子类对象时,分以下几步走:
- 开辟内存空间
- 构造父类
- 在类的首地址处,填入编译时创建完毕的虚函数表的地址,即虚函数表指针
- 进入类的构造函数,执行初始化列表
- 执行构造函数body部分
就如上面那段代码,汇编语言:
⭕平时做题时经常遇到,在父类的构造函数中,通过指向子类的父类this指针,调用了虚函数,且子类中重写了该虚函数。但是,此时子类的虚函数表并未明确(虚函数表指针尚未填入),所以不会触发多态,还是会调用父类的虚函数。
补充:
初始化列表的初始化顺序与初始化列表顺序无关。
- 对于成员变量,按成员变量的声明顺序进行初始化。
- 对于父类构造,按继承的声明顺序正向进行构造。
而对于子类调用析构后,自动调用父类的析构,对于多个父类,安装继承的声明顺序反方向进行析构。
#include <iostream>using namespace std;class Parent
{
public:Parent(){cout << "Parent()" << endl;}~Parent(){cout << "~Parent()" << endl;}
};class Base
{
public:Base(){cout << "Base()" << endl;}~Base(){cout << "~Base()" << endl;}
};class Child :public Parent, public Base
{
public:Child():Base(),Parent(){cout << "Child()" << endl;}~Child(){cout << "~Child()" << endl;}
};int main()
{Child c;return 0;
}