c++中虚多态的实现机制

c++中虚多态的实现机制


參考博客:http://blog.csdn.net/neiloid/article/details/6934135

  1. 序言
  2. 证明vptr指针存在
  3. 无继承
  4. 单继承无覆盖
  5. 单继承有覆盖
  6. 多继承有覆盖
  7. 总结

1.序言
这篇博文探讨c++内部多态的实现机制,參考书主要是《深入探索c++对象模型》。因为本人水平有限,望读者指正。


实现多态有3点前提,即继承。虚化,基类指针指向派生类对象。
本质而言,整体来说,当类的内部通过virtual关键字声明虚函数时。编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在这里)。

这样。就能够通过指针来调用这些函数。


PS:执行环境为VC++6.0。因为编译器的差异而产生的不同本文不讨论。


大概的内存布局


2.证明vptr指针的存在

#include<iostream>
using namespace std;
class Base
{
public:
    virtual void f(){cout<<"Base::f()"<<endl;}
    virtual void g(){cout<<"Base::g()"<<endl;}
    virtual void h(){cout<<"Base::h()"<<endl;}
};
int main (void)
{
    Base b; 
    cout<<"证明vptr的存在"<<endl;
    cout<<"sizeof b = " <<sizeof b<<endl; 
    return 0;
}

执行结果
ps:linux下 sizeof b = 8


3无继承

#include<iostream>
using namespace std;
class Base
{
public:
    virtual void f(){cout<<"Base::f()"<<endl;}
    virtual void g(){cout<<"Base::g()"<<endl;}
    virtual void h(){cout<<"Base::h()"<<endl;}
};

int main (void)
{
    typedef unsigned long P_FUN;//指针类型为无符号长整型
    typedef void(*FUN)(void);//声明函数指针

    FUN f,g,h;
    Base b; 

    f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
    g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
    h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));

    f();
    g();
    h();

    cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+3))<<endl;
    return 0;
}

执行结果
这样就通过指针的方式调用了续表中的方法函数。


通过现象。能够大概预计出内存的布局。例如以下图。
结束位置以后可能为操作系统内部的仅仅读空间,訪问会发生段错误。


ps:linux下结束位置可能为0。可能为1,有兴趣的读着能够研究一下。


内存布局


4.单继承无覆盖

#include<iostream>
using namespace std;
class Base
{
public:
    virtual void f(){cout<<"Base::f()"<<endl;}
    virtual void g(){cout<<"Base::g()"<<endl;}
    virtual void h(){cout<<"Base::h()"<<endl;}

};
class Derive:public Base
{
public:
    virtual void f1(){cout<<"Derive::f1()"<<endl;}
    virtual void g1(){cout<<"Derive::g1()"<<endl;}
    virtual void h1(){cout<<"Derive::h1()"<<endl;}

};
int main (void)
{
    typedef unsigned long P_FUN;
    typedef void(*FUN)(void);

    FUN f,g,h,f1,g1,h1;
    Derive b;

    f=(FUN)(*(P_FUN*)*(P_FUN*)&b);
    g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
    h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
    f1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
    g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));
    h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+5));

    f();
    g();
    h();
    f1();
    g1();
    h1();

    cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+6))<<endl;
    return 0;
}

执行结果
依据结果能够看出,无覆盖的情况下,派生类的虚函数直接放在第一张虚表后边。
内存布局


5.单继承有覆盖

#include<iostream>
using namespace std;
class Base
{
public:
    virtual void f(){cout<<"Base::f()"<<endl;}
    virtual void g(){cout<<"Base::g()"<<endl;}
    virtual void h(){cout<<"Base::h()"<<endl;}

};
class Derive:public Base
{
public:
    virtual void f(){cout<<"Derive::f1()"<<endl;}
    virtual void g1(){cout<<"Derive::g1()"<<endl;}
    virtual void h1(){cout<<"Derive::h1()"<<endl;}

};
int main (void)
{
    typedef unsigned long P_FUN;
    typedef void(*FUN)(void);

    FUN f,g,h,f1,g1,h1;
    Derive b;

    f1=(FUN)(*(P_FUN*)*(P_FUN*)&b);
    g=(FUN)(*((P_FUN*)*(P_FUN*)&b+1));
    h=(FUN)(*((P_FUN*)*(P_FUN*)&b+2));
    g1=(FUN)(*((P_FUN*)*(P_FUN*)&b+3));
    h1=(FUN)(*((P_FUN*)*(P_FUN*)&b+4));

    f1(); 
    g();
    h();
    g1();
    h1();

    cout<<(FUN)(*((P_FUN*)*(P_FUN*)&b+5))<<endl;
    return 0;
}

执行结果
依据执行结果能够看出,有覆盖的情况下,派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。
内存布局

6多继承有覆盖

#include<iostream>
using namespace std;
class Base1
{   
private:
    virtual void f(){cout<<"Base1::f()"<<endl;}
    virtual void g(){cout<<"Base1::g()"<<endl;}
    virtual void h(){cout<<"Base1::h()"<<endl;}
};
class Base2
{
public:
    virtual void f(){cout<<"Base2::f()"<<endl;}
    virtual void g(){cout<<"Base2::g()"<<endl;}
    virtual void h(){cout<<"Base2::h()"<<endl;}
};
class Derive:public Base1,public Base2
{
public:
    virtual void f(){cout<<"Derive::f1()"<<endl;}
    virtual void g1(){cout<<"Derive::g1()"<<endl;}
    virtual void h1(){cout<<"Derive::h1()"<<endl;}
};
int main (void)
{
    typedef unsigned long P_FUN;
    typedef void(*FUN)(void);

    FUN B1f,B1g,B1h,B2f,B2g,B2h,Dg,Dh;
    Derive b;

    B1f=(FUN)(*(P_FUN*)*((P_FUN*)&b));
    B1g=(FUN)(*((P_FUN*)*((P_FUN*)&b)+1));
    B1h=(FUN)(*((P_FUN*)*((P_FUN*)&b)+2));

    B2f=(FUN)(*(P_FUN*)*((P_FUN*)&b+1));
    B2g=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+1));
    B2h=(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+2));

    Dh=(FUN)(*((P_FUN*)*((P_FUN*)&b)+3));
    Dg=(FUN)(*((P_FUN*)*((P_FUN*)&b)+4));

    B1f();
    B1g();
    B1h();
    cout<<endl;

    Dh();
    Dg();
    cout<<endl;

    B2f();
    B2g();
    B2h();
    cout<<endl;

    cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b)+5))<<endl;
    cout<<(FUN)(*((P_FUN*)*((P_FUN*)&b+1)+3))<<endl;

    return 0;
}

执行结果
依据结果可知,多继承下,覆盖的派生类函数会把全部的虚表中相相应的基类虚函数覆盖掉,不覆盖的派生类虚函数仅仅放在第一张虚表的后面。其它虚表后没有。
内存布局


7.总结
1.当类的内部通过virtual关键字声明虚函数时,编译器生成vptr指针,指向虚函数表(虚表),虚表中依次存放着各虚函数(非虚函数不在当中)。


2.无覆盖的情况下。派生类的虚函数直接放在第一张虚表后边。
3.有覆盖的情况下。派生类虚函数覆盖到基类的虚函数,不覆盖的虚函数依次放在第一张表的后面。


4.多继承下,覆盖的派生类函数会把全部的虚表中相相应的基类虚函数覆盖掉,不覆盖的派生类虚函数仅仅放在第一张虚表的后面,其它虚表后没有。

PS:c++是一门威力十分强大的语言。内部构造复杂,或许有人说这些内容没什么卵用,但本人认为深入理解一下绝不是坏事,本文阅读的难点在于指针的应用,事实上整个c/c++的强大也是得益于指针,希望大家通过本文对c/c++有更深入的理解。
另外说明一下。指针确实是不安全的,比如本文中全部的public关键字删掉之后,通过指针仍然能够调用出类内的私有成员方法。

本人是一个即将大三的本科生,能力有限。博文如有不妥支出望各位读者指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值