基类指针(或函数)调用派生类中与基类同名函数时,它只能看到基类的函数,
看下面函数:
class A {
public:
virtual void fun0() { cout << "A::fun0" << endl; }
int a;
int b;
};
int main(int argc, char* argv[])
{
A a;
cout << "Size of A = " << sizeof(a) << endl;
return 0;
}
结果如下:Size of A = 12 其虚函数在内存中结构是这样的:
如果有两个虚函数时VPTR指针如下:
再看下面的程序:
class A {
public:
virtual void f() { }
};
class B {
public:
virtual void f() { }
};
class C {
public:
virtual void f() { }
};
class Drive : public A, public B, public C {
};
int main() {
Drive d;
cout << "Size is = " << sizeof(d) << endl;
return 0;
}
结果如下:Size is = 12 ,我们可以看看它的内存结构图:
虚函数可以实现函数的多态性,多态性是指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。C++程序在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
#include <iostream.h >
class shape{
public:
virtual void draw(){cout<<"shape::draw()"<<endl;}
virtual void area(){cout<<"shape::area()"<<endl;}
void fun(){draw();area();}
};
class circle:public shape{
public:
void draw(){cout<<"circle::draw()"<<endl;}
void adjust(){cout<<"circle::adjust()"<<endl;}
};
main(){
shape oneshape;
oneshape.fun();
circle circleshape;
shape& baseshape=circleshape;
baseshape.fun();
}
在上面的程序中,this指针不同,从而连接到不同的fun函数。那么C++如何编译这些指令呢。道理在于:所有的基类的派生类的虚拟函数表的顺序与基类的顺序是一样的,对于基类中不存在方法再按照声明次序进行排放。这样不管是shape还是circle或者从shape又继承出来的其余的类它们的虚拟函数表的第一项总是draw函数的地址,然后是area的地址。对于circle类,下面的才是adjust的地址。因此不管对于shape还是circle,this->draw总是编译成 call this->VTABLE[0]; this->area()总是翻译成 call this->VTABLE[1]; 程序到真正运行时候将会发现this的真正指向的对象,如果是shape,则调用shape->VTABLE[0],如果是circle,则调用circle->VTABLE[1],如下图:
参考:
1. Zeeshan Amjad写的"ATL on the Hood”
2. 解析动态联(下)