C++中的多态就是依据虚函数实现的,那么下面借用一个例子来说一下虚函数是怎样实现多态的,并解释一下虚函数到底是怎样实现的。
class ClassA
{
public:
void fun1();
void fun2();
virtual void fun3();
};
void ClassA::fun1()
{
cout<<"ClassA.fun1"<<endl;
};
void ClassA::fun2()
{
cout<< "ClassA.fun2"<<endl;
};
void ClassA::fun3()
{
cout<< "ClassA.fun3"<<endl;
};
class ClassB : public ClassA
{
public:
void fun1();
void fun2();
virtual void fun3();
};
void ClassB::fun3()
{
cout<< "ClassB.fun3"<<endl;
};
class ClassC : public ClassB
{
public:
void fun1();
void fun2();
virtual void fun3();
};
void ClassC::fun3()
{
cout<< "ClassC.fun3"<<endl;
};
void main()
{
ClassA *a[3];
ClassA a1;
ClassB b1;
ClassC c1;
a1.fun3();
b1.fun3();
c1.fun3();
a[0] = &a1;
a[1] = &b1;
a[2] = &c1;
cout << "virtual function array test" <<endl;
for(int i=0;i<3;i++)
{
a[i]->fun3();
}
cout << "((ClassA)&b1).fun3():";
((ClassA*)&b1)->fun3();
//object slicing
cout << "object slicing"<<endl;
cout <<"((ClassA)b1).fun3():";
((ClassA)b1).fun3();
}
这三个类之间的继承关系是:ClassC继承ClassB,ClassB继承ClaaA,fun3是虚函数,并且在ClassB和ClassC中被重写。
下面先介绍一下什么是虚函数:
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数。用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};
多态性的实现是通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。
接下来说一下虚函数是怎样实现多态的以及虚函数的实现机制:实际上,编译器是通过一个虚函数表,在运行时间接的调用实际上绑定的函数来达到动态绑定的,这张表对我们来说是透明的,是编译器
针对那些
包含虚函数的代码自动加上去的。该表每一行的元素是一个指向代码中虚函数的指针,有了这个地址,我们可以调用实际代码中的虚函数了。编译器又通过增加一个指向该虚函数表的指针(vptr)将该表与我们的代码联系到一起。
ClassA中vptr与虚函数表之间的关系图(盗的图)
每一个ClassA的实例,都会有一个虚函数表vptr,当我们在代码中通过这个实例来调用虚拟函数时,都是通过vptr先找到虚拟函数表,
接着
在
虚函数表中再找出指向的某个真正的虚拟函数地址。虚拟函数表中的内容就是类中按顺序声明的虚拟函数组织起来的。在派生的时候,子类都会继承父类的虚拟函数表vptr,若子类改写了父类中的虚拟函数,则子类的vptr成员也会作修改,此时,子类的vptr成员指向的是子类所改写父类的
虚拟函数地址。多态也就体现在这里。
ClassB中vptr与虚函数表之间的关系图
ClassC中的关系也同理。
那么,一个指向ClassA的对象的实例,调用fun3就是ClassA::fun3(),一个指向ClassB的对象的实例,调用fun3就是ClassB::fun3(),一个
指向
ClassC的对象的实例,调用fun3就是ClassC::fun3(),这些调用通过都是通过虚函数表来进行的。上面的结果是:
ClassA.fun3 ClassB.fun3 ClassC.fun3 virtual function array test ClassA.fun3 ClassB.fun3 ClassC.fun3 ((ClassA)&b1).fun3():ClassB.fun3 object slicing ((ClassA)b1).fun3():ClassA.fun3 请按任意键继续. . .