例子引出
#include <iostream>
using namespace std;
class Parent
{
public:
Parent(int a = 0)
{
this->a = a;
}
virtual void print()
{
cout<<"Parent"<<endl;
}
protected:
private:
int a;
};
class Child :public Parent
{
public:
Child(int a = 0,int b = 0):Parent(2)
{
this->b = b;
}
void print()
{
cout<<"Child"<<endl;
}
protected:
private:
int b;
};
void howToPrint(Parent *p)
{
p->print();
}
int main()
{
Parent p;
Child c;
//cout<<sizeof(p)<<endl;//4 Parent类和Child类不加virtual关键字时
//cout<<sizeof(c)<<endl;//8
cout<<sizeof(p)<<endl;//8 当Parent类的print函数加virtual关键字时
cout<<sizeof(c)<<endl;//12 Parent类和Child类的大小都增加了4个字节===vptr指针
howToPrint(&p);
howToPrint(&c);
system("pause");
return 0;
}
思考:C++是如何实现多态的呢?当我们不加virtual关键字的时候:
cout<<sizeof(p)<<endl;//4 Parent类和Child类不加virtual关键字时
cout<<sizeof(c)<<endl;//8
sizeof(p)是4个字节 sizeof(c)是8个字节,这很符合常理。但是当我们再父类Parent的print函数前加上virtual关键字的时候,
cout<<sizeof(p)<<endl;//8 当Parent类的print函数加virtual关键字时
cout<<sizeof(c)<<endl;//12 Parent类和Child类的大小都增加了4个字节===vptr指针
结果都增加了4个字节,这是为什么呢?
猜测,既然加上关键字virtual C++编译器实现了多态,那么这增加的4个字节很有可能有多态实现机制有关。
那么,让我们带着这个猜测去证明我们的设想!
说明,当类中声明虚函数时,编译器会在类中生成一个虚函数表
虚函数表是一个存储类成员函数指针的数据结构
虚函数表是由编译器自动生成与维护的
virtual成员函数会被编译器放入虚函数表中
存在虚函数时,每个对象中都有一个指向虚函数表的指针VPTR指针
说明1:通过虚函数表指针VPTR调用重写函数是在程序运行时进行的(动态联编),因此需要通过寻址操作才能确定真正应该调用的函数。
而普通成员函数是在编译时就确定了调用的函数,在效率上,虚函数的效率要低很多。
说明2:出于效率考虑,没有必要将所有成员函数都声明为虚函数。
说明:程序运行期间,当p指针指向Parent对象时,指针通过父类对象的VPTR指针查找父类对象的VTABLE找到要执行的函数的入口地址,调用父类的函数。
同理,当p指针指向Child对象时,指针通过子类对象的VPTR指针查找子类对象的VTABLE找到要执行的函数的入口地址,调用子类的函数。从而,实现了多态。