说明:关于虚函数的基本语法知识就不再叙述,直接讲其内部原理。
(1)单继承下内存布局
举例:
类Point:
class Point {
public:
virtual ~Point();
virtual Point& mult( float ) = 0;
float x() const { return _x; } //非虚函数,不作存储
virtual float y() const { return 0; }
virtual float z() const { return 0; }
// ...
protected:
Point( float x = 0.0 );
float _x;
};
类Point2d:
class Point2d : public Point {
public:
Point2d( float x = 0.0, float y = 0.0 )
: Point( x ), _y( y ) {}
~Point2d(); //1
//改写base class virtual functions
Point2d& mult( float ); //2
float y() const { return _y; } //3
protected:
float _y;
};
类Point3d:
class Point3d: public Point2d {
public:
Point3d( float x = 0.0,
float y = 0.0, float z = 0.0 )
: Point2d( x, y ), _z( z ) {}
~Point3d();
// overridden base class virtual functions
Point3d& mult( float );
float z() const { return _z; }
// ... other operations ...
protected:
float _z;
};
上述类对象布局如下:
解释:
(1)对于上面的布局Point2d对象虚函数指针所指的虚基表中首先是自身的析构函数(基类的析构函数无法继承到子类),然后是本身覆盖的基类的实现mult和y函数,最后一个槽位是从基类中继承而来的函数z(Point2d中没有对此函数进行重新定义)。 对于Point3d同理。_vptr_Point 所指向哪个虚表又编译器在构造函数执行期间指定,所以说构造函数不能为虚函数,因为执行构造函数时虚基表还没有形成,虚函数机制还未成形。
(2) 在执行期间是如何知道执行基类的虚函数版本还是派生类的虚函数版本呢? 在解决这个问题前请先了解下基类和派生类之间的指针转换:指针之间转换
如果执行如下操作:
Point p; // 在上文中这样声明不合法(因为有纯虚函数)这里只是举例
Point *pd;
Point2d p2d;
pd = &p2d;
pd ->y()
那么在执行 pd = &p2d; 到基类转化的时候其实是 截取了p2d中 属于Point的部分。此时_vptr_Poin所指向的虚基表t并没有改变,还是如上图所示。那么在执行pd->y()时其实是执行的派生类中的虚函数。
(2) 多继承下的内存布局
class Base1
{
public:
virtual ~Base1() {};
virtual void speakClearly() {cout<<"Base1::speakClearly()"<<endl;}
virtual Base1 *clone() const {cout<<"Base1::clone()"<<endl; return new Base1;}
protected:
float data_Base1;
};
class Base2
{
public:
virtual ~Base2() {};
virtual void mumble() {cout<<"Base2::mumble()"<<endl;}
virtual Base2 *clone() const {cout<<"Base2::clone()"<<endl; return new Base2;}
protected:
float data_Base2;
};
class Derived : public Base1,public Base2
{
public:
virtual ~Derived() {cout<<"Derived::~Derived()"<<endl;}
virtual Derived *clone() const {cout<<"Derived::clone()"<<endl; return new Derived;}
protected:
float data_Derived;
};
上例中内存布局如下
(3)虚继承下内存布局
class Point2d
{
public:
Point2d (float = 0.0, float = 0.0){}
virtual ~Point2d(){}
virtual void mumble(){}
virtual float z(){ return 0;}
protected:
float _x, _y;
};
class Point3d : public virtual Point2d
{
public:
Point3d(float = 0.0, float = 0.0, float = 0.0 ){}
~Point3d(){}
float z(){return _z;}
protected:
float _z;
};

附录:VC中查看虚函数表
(转自:http://blog.youkuaiyun.com/haoel/article/details/1948051)
我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了(并不是很完整的)