class Shape
{
public:
    virtual  void draw(){}
    virtual void color(){}
private:
    int m_data1;
    int m_data2;
};
class Circle:public  Shape
{
public:
    void draw()
    {
        cout << "画圆" << endl;
    }
private:
    int m_data3;
};
class Ellipse :public Circle
{
public:
    void draw()
    {
        cout << "画椭圆" << endl;
    }
private:
    int m_data1;  //与shape中的数据同名
    int m_data4;
};

  在开发人员命令提示工具中输入命名: cd 源代码所在的文件地址  dir 回车  cl /d1 reportSingleClassLayout类名  对象.cpp可以查看虚函数指针和虚函数表。

class Shape     size(12):
        +---
 0      | {vfptr}
 4      | m_data1
 8      | m_data2
        +---

Shape::$vftable@:
        | &Shape_meta
        |  0
 0      | &Shape::draw
 1      | &Shape::color

Shape::draw this adjustor: 0
Shape::color this adjustor: 0



class Circle    size(16):
        +---
        | +--- (base class Shape)
 0      | | {vfptr}
 4      | | m_data1
 8      | | m_data2
        | +---
12      | m_data3
        +---

Circle::$vftable@:
        | &Circle_meta
        |  0
 0      | &Circle::draw
 1      | &Shape::color

Circle::draw this adjustor: 0



class Ellipse   size(24):
        +---
        | +--- (base class Circle)
        | | +--- (base class Shape)
 0      | | | {vfptr}
 4      | | | m_data1
 8      | | | m_data2
        | | +---
12      | | m_data3
        | +---
16      | m_data1
20      | m_data4
        +---

Ellipse::$vftable@:
        | &Ellipse_meta
        |  0
 0      | &Ellipse::draw
 1      | &Shape::color

Ellipse::draw this adjustor: 0

  画成图表示

 

   由此可见:父类中写了一个虚函数,类的内部发生了改变,多了一个指针(4B)vptr(虚函数指针),指向一个虚函数表,表内写的是虚函数的入口地址,当子类继承父类的时候,子类的虚函数表和父类的虚函数表一样,当子类重写父类虚函数时候,会把父类的函数入口替换成自己的函数入口。

  动态绑定

1     Circle circle;
2     Shape shape = (Shape)circle;
3     shape.draw();      //shape是一个对象不是指针,所以是静态绑定
4 
5     Shape *pa = new Circle();  //向上转型
6     pa->draw(); //动态绑定

  之所以是动态绑定,是因为编译器会把第六行代码转换成(*p->vptr[n])(p),通过指针p(p指向Circle类型)找到Circle的虚函数指针,通过虚函数指针找到虚函数表,取出表中第n个函数地址,通过函数指针来调用,再p当成this指针传入函数中。