前几天面试的时候被问到了多态的实现,平时确实没有用到过。今天特意去看了相关的知识,并实现了一下,来看一看输出结果,并分析一下导致这样的结果的原因。
#include "stdafx.h"
#include<iostream>
using namespace std;
class Father
{
public:
virtual void TestV()
{
cout << "father.virtual test" << endl;
}
void Test()
{
cout << "father.normal test" << endl;
}
};
class Son1:public Father
{
void TestV()
{
cout << "son1.virtual test" << endl;
}
void Test()
{
cout << "son1.normal test" << endl;
}
};
class Son2 :public Father
{
void TestV()
{
cout << "son2.virtual test" << endl;
}
void Test()
{
cout << "son2.normal test" << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Son1 son1;
Son2 son2;
Father *pf1 = &son1;
Father *pf2 = &son2;
pf1->TestV();
pf2->Test();
return 0;
}
Father *pf = &son;这样的语句我本来以为是定义了一个父类对象指针,指向子类对象,所以这个pf应该是一个Son的对象,所以在调用Test方法时,应该是调用子类的方法。 结果如下:
但事实上,只有TestV()这个方法,这个重写了父类的虚函数的方法是调用到了子类的方法,而普通的Test()方法,确是调用的父类的方法。
这就说明,这个virtual关键字,确实起到了他的作用。(废话),这里为什么结果会这样,已经不是我能够想清楚的了。参考另一篇博客的内容就能理解了。
这里涉及到了两个概念:早绑定,晚绑定
早绑定:C++编译器在编译的时候,需要确定每一个对象调用的函数(非虚函数)的地址。Father *pf = &son;通过这个语句,我们把Son类的对象son的地址赋值给力pf时,C++编译器进行了隐式的类型转换,编译器就认为pf保存的是Father对象的地址,所以调用的就是Father对象的函数。但实际上,pf指向的是Son类的对象,希望输出的是Son类的方法,这个时候就要用到虚函数,采用晚绑定来实现了。
晚绑定:在基类中声明函数时使用virtual关键字来告诉编译器进行晚绑定。即不在编译阶段确定对象调用函数的地址,而是在运行时再去确定对象的类型以及正确的调用子类的函数。
这里面还涉及到了虚表,虚表指针
编译器会为每一个包含虚函数的类创建一个虚表(一维数组),存放虚函数的地址。为每个对象提供了一个虚表指针,指向该对象所属类的虚表。在程序运行时,根据对象类型去初始化虚表指针,从而让虚表指针指向了所属类的续表,从而在调用虚函数的时候,能够找到正确的函数。虚表的创建和虚表指针的初始化是在构造函数中进行的。