今天在面试过程中被问到C++的多态,居然被问得哑口无言。回想面试官的对话,再总结一下关于多态。
比较好的博文是这几篇,不想听我讲面试经历的可以直接去看:
1、http://blog.csdn.net/hackbuteer1/article/details/7475622
2、http://blog.csdn.net/tujiaw/article/details/6753498
先总结要点:
1、C++是通过迟绑定late binding来实现多态。所谓迟绑定,即运行时才决定调用的函数地址。反之早绑定是编译时就知道函数地址。
2、每个类都有vtable,只有带virtual的虚函数才跟vtable相关。不带virtual的,按名字覆盖(这里有可能装点小机关,故意不加virtual)。
3、要从C++的设计理念出发,多态是为了统一对外接口(父类,或带有纯虚函数的抽象类)。但是真正调用时却是调了子类的方法。
4、假设父类A有虚方法virtual fun(){printf("A");} 子类B也实现了该方法fun(){printf("B");}。那么多态就是为了让以A的统一接口,却是运行B的方法。
具体说来,先建一个B的对象b。
B b;
再造一个A的指针pa,指向b。
A* pa = &b;
所以对外接口看起来就是A类型,但是实际执行pa->fun时却真实执行了B的fun。
同理,做一个A类型的引用aa,指向b,也是一样的结果
A& aa = b;
5、要解释这个现象,参考资料【http://blog.csdn.net/tujiaw/article/details/6753498】所说的,“每个对象调用的虚函数都是通过虚表指针来索引的...虚表指针的正确初始化是非常重要...虚表指针在什么时候什么地方初始化呢?答案是在构造函数中进行虚表的创建和虚表指针的初始化...
构造函数的调用顺序,在构造子类对象时,要先调用父类的构造函数,此时编译器只“看到了”父类,并不知道后面是否后还有继承者,它初始化父类对象的虚表指针,该虚表指针指向父类的虚表。当执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。”
所以上述B b先调了一次A构函,再调B的构函,然后确定自己的vtable(各虚函数的调用偏置值)。
“在程序中,不管你的对象类型如何转换,但该对象内部的虚表指针是固定的,所以呢,才能实现动态的对象函数调用,这就是C++多态性实现的原理。”
具体面试过程:
首先是定义,面试官让我解释一下多态,我回答说父类是动物,分别有子类猫、狗。面试官说这只解释了继承。我马上补充,应该是动物有一个“叫”的virtual method。猫、狗都实现这个虚函数。用户调用,根据输入参数的不同,响应出不同的“叫”。如果这个函数的原型没有参数呢?其实默认带了一个this指针。
class Animal
{
public:
virtual void cry(){ printf("Animal cry\n");}
};
class Dog: public Animal
{
public:
void cry(){ printf("Dog cry\n");}
}
class Cat: public Animal
{
public:
void cry(){ printf("Cat cry\n");}
}