C/C++—— 在构造函数中调用虚函数能实现多态吗(Vptr指针初始化的过程分析)

时间:2022-05-03 19:47:07

问题引入:

比如:如果我们想在父类的构造函数中调用虚函数,当定义子类对象的时候,父类的构造函数中的虚函数执行的是子类中的函数。
在下面的例子中,定义子类对象的时候,在父类构造函数中的print虚函数执行的不是子类中的print函数,而是父类中的print函数。

#include <iostream>
using namespace std;

class Parent
{
public:
//在父类的构造函数里面,调用虚函数,不会产生多态。。
//言外之意:不会调用子类的虚函数。。。。
Parent(int a = 0)
{
print(); //定义子类对象的时候,想该print函数调用的是子类的print函数。。结果表明调用的是父类的print函数
this->a = a;
}

//第一个动手脚的地方 编译器应该对这个虚函数特殊处理。。。。
virtual void print()
{
cout<< "Parent." <<endl;
}
private:
int a;
};

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

int main()
{
Child c1;
return 0;
}

输出:
Parent.
输出为Parent.表明调用的是父类的虚函数print,不是子类的虚函数print。

原因分析:

参考博客: C/C++—— C++编译器是如何实现多态

主要是参考该博客的最后一部分分析:对象中的VPTR指针什么时候被初始化?

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

当定义一个子类对象的时候比较麻烦,因为构造子类对象的时候会首先调用父类的构造函数然后再调用子类的构造函数。当调用父类的构造函数的时候,此时会创建Vptr指针(也可以认为Vptr指针是属于父类的成员,所以在子类中重写虚函数的时候virtual关键字可以省略,因为编译器会识别父类有虚函数,然后就会生成Vptr指针变量),该指针会指向父类的虚函数表;然后再调用子类的构造函数,此时Vptr又被赋值指向子类的虚函数表。
(执行父类的构造函数的时候Vptr指针指向的是父类的虚函数表,所以只能执行父类的虚函数)
上面的过程是Vptr指针初始化的过程。
这是因为这个原因,在构造函数中调用虚函数不能实现多态。