C++中的vptr指针
若类中包含虚函数,则编译器会在类实例化对象时在对象中加入vptr指针,它指向一个虚函数表,子类和父类分别有自己的虚函数表,所以使用父类指针调用类的虚函数时,是根据实际的对象时子类对象还是父类对象,来实现虚函数的调用。
引入vptr指针
demo.cpp:
#include <iostream>
//多态成立的三个条件
//要有继承 虚函数重写 父类指针指向子类对象
using namespace std;
class Parent
{
public:
Parent(int a = 0)
{
this->a = a;
}
virtual void print()//动手脚1,使用virtual关键字,编译器会特殊处理
{
cout << "我是爹" <<endl;
}
private:
int a;
};
class Child : public Parent
{
public:
Child(int a = 0, int b = 0):Parent(a)
{
this->b = b;
}
virtual void print()
cout << "我是儿子" <<endl;
}
private:
int b;
};
void HowToPlay(Parent * base)
{
base->print();//动手脚 2
/*传来子类对象,执行子类的print函数,传来父类对象
执行父类print函数,其实C++编译器根本不需要区分是子类
对象还是父类对象,父类对象和子类对象分别有vptr指针,指向
虚函数表,表中存储函数入口地址,实现迟绑定(运行时,C++程序采取判断)
*/
}
int main()
{
Parent p1(2);//动手脚 3(提前布局)
//用类实例化对象时,C++编译器会在对象中添加一个vptr指针
//vptr指向虚函数表,此表由编译器生成和维护
Child c1;
HowToPlay(&p1);
HowToPlay(&c1);
}
下面我们来证明vptr指针的存在
demo2.cpp:
#include <iostream>
using namespace std;
class Parent
{
public:
virtual void print()
{
cout << "hello" << endl;
}
private:
int a;
};
class Parent2
{
public:
void print()
{
cout << "hello" << endl;
}
private:
int a;
};
int main()
{
void * p;
cout << "sizeof(Parent):" << sizeof(Parent) << endl;
cout << "sizeof(Parent2):" << sizeof(Parent2) << endl;
}
输出结果为:
sizeof(Parent):8
sizeof(Parent2):4
Parent类比Parent2多4个字节,这4个字节就是vptr指针所占的空间。
vptr指针的分步初始化
demo3.cpp:
#include <iostream>
using namespace std;
//vptr指针的分步初始化
//构造函数中调用虚函数能实现多态吗(不能)
//1.要初始化c1.vptr指针,初始化是分步的
//2.当执行父类的构造函数时,c1.vptr指向父类的虚函数表,
// 当父类构造函数执行完毕后,会把c1.vptr指向子类的虚函数表
//3.结论:子类的c1.vptr指针分步完成
class Parent
{
public:
Parent(int a = 0)
{
this->a = a;
print();//这里并不能发生多态,调用的仍然是父类的print,
//结果是显而易见的,此时子类还没有构建好,成员变量
//还没初始化,如果调用子类的函数,是不合情理的
}
virtual void print()
{
cout << "我是爹" <<endl;
}
private:
int a;
};
class Child : public Parent
{
public:
Child(int a = 0, int b = 0):Parent(a)
{
this->b = b;
print();
}
virtual void print()
{
cout << "我是儿子" <<endl;
}
private:
int b;
};
int main()
{
Child c1;
}