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关键字删掉之后,通过指针仍然能够调用出类内的私有成员方法。

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值