以这段代码为例。
#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;
}