深度探索C++对象模型之:理解虚函数机制

说明:关于虚函数的基本语法知识就不再叙述,直接讲其内部原理。

(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.csdn.net/haoel/article/details/1948051)

 

我们可以在VCIDE环境中的Debug状态下展开类的实例就可以看到虚函数表了(并不是很完整的)


        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值