C++中多态的实现原理
- 当类中声明虚函数时,编译器会在类中生成一个虚函数表;
- 虚函数表是一个存储类成员函数指针的数据结构;
- 虚函数表是由编译器自动生成与维护的;
- virtual成员函数会被编译器放入虚函数表中;
- 存在虚函数时,每个对象中都有一个指向虚函数表的指针(
vptr
指针)。
C++编译器为了实现多态会提前布局,在编译阶段就会为含有虚函数的对象添加一个vptr
指针;因此,C++编译器根本不需要区分父类和子类,只需根据vptr
指针指向的虚函数就可以调用对应的函数而实现多态。
证明vptr
指针存在:
#include <iostream>
using namespace std;
class Base1
{
public:
void print()
{
cout << "Base1" << endl;
}
void print2()
{
cout << "Base1" << endl;
}
private:
int a;
};
class Base2
{
public:
virtual void print()
{
cout << "Base2" << endl;
}
virtual void print2()
{
cout << "Base2" << endl;
}
private:
int a;
};
int main()
{
cout << sizeof(Base1) << endl;
cout << sizeof(Base2) << endl;
return 0;
}
4
8
vptr
指针占4个字节
构造函数中调用虚函数不能发生多态,因为vptr
指针的初始化是分步的
例如:执行Child c1
时
- 先调用父类的构造函数,这时
c1.vptr
指针指向父类的虚函数表; - 当父类的构造函数运行完毕后,会把
c1.vptr
指针指向子类的虚函数表。
结论:子类的c1.vptr
指针分步初始化。
子类指针的步长和父类不一样,当子类添加新的成员和方法时,步长就会比父类长。
Parent* pP = NULL;
Child* pC = NULL;
Child array[] = { Child(1),Child(2),Child(3) };
pP = array;
pC = array;
pP++;
pC++;//此时步长不一致
pP->print();
pC->print();//多态发生,但由于步长不一致会导致程序崩溃