C++对象模型(一)单继承

1.无虚函数的单继承

class A
{
public:
    A():_ai(1){}
    void Afun();
    static void static_fun();
    static int _val;
private:
    int _ai;
};

class B:public A
{
public:
    B():_bi(2){}
    void Bfun();
private:
    int _bi;
};

int main()
{
    A a;
    B b;
    cout<<sizeof(A)<<endl;//4
    cout<<sizeof(B)<<endl;//8
    return 0;
}

上述代码内存分布:
这里写图片描述
我们可以调试在内存中看一下:(VS2008调试,本文后续都在此环境)
这里写图片描述

2.有虚函数的单继承

虚函数是C++多态实现的一种形式,那么,在虚函数中,编译器是怎么知道什么时候调用合适的函数呢?
编译器为每个包含虚函数的类创建一个表(虚函数表/VTABLE)。虚表中存放着类里面虚函数的地址。在每个虚函数的类中,有一个指针指向虚表,称为vpointer(VPTR),这个指针指向虚表。当进行多态调用时,编译器通过VPTR在虚表中通过函数地址来查找函数代码,这样就能正确的调用函数实行动态编译。看下面代码:

class A
{
public:
    A():_a(1){}
    virtual void fun1(){};
    virtual void fun2(){};
private:
    int _a;
};

int main()
{
    A a;
    cout<<sizeof(A)<<endl;
    return 0;
}

类A的大小为8说明了A里面还有别的东西,在此假设为VPTR。调试上述代码 ,查看对象a的内存:
这里写图片描述
可以看到 ,对象a的地址开始4个字节处存放的是VPRT。
把VPRT的那串地址拿出来在内存中look一下,VS2008为小端存储
这里写图片描述
那我们不妨通过虚表里面的函数地址调用一下虚函数:

typedef void (* pfun)();
void PrintVirtual(pfun *_pfun)  
{  
   while(*_pfun)
   {  
       (*_pfun)(); 
       ++_pfun;
   }  
}  
void test()
{
    A a;
    pfun* p = (pfun*)*((int*)(&a));//拿出对象a前4个字节,把它强转为函数指针

    PrintVirtual(p);
}

成功的通过虚表调用了对象a里面的虚函数:
这里写图片描述

清楚了以上的虚函数表,下面研究有虚函数的单继承,看代码:

class Base
{
public:
    Base():_b(1){}
    virtual void fun1()
    {
        cout<<"Base::fun1()"<<endl;
    }
    virtual void fun2()
    {
        cout<<"Base::fun2()"<<endl;
    }
private:
    int _b;
};

class Derive:public Base
{
public:
    Derive():_d(2){}
    virtual void fun1()
    {
        cout<<"Derive::fun1()"<<endl;
    }
    virtual void fun3()
    {
        cout<<"Derive::fun3()"<<endl;
    }
    virtual void fun4()
    {
        cout<<"Derive::fun4()"<<endl;
    }
private:
    int _d;
};

int main()
{
    Base b;
    Derive d;
    cout<<sizeof(Base)<<endl;//8
    cout<<sizeof(Derive)<<endl;//12
    return 0;
}

按照前面虚表的知识,上面代码中的理论上派生类Derive的大小是基类大小+派生类成员大小+派生类VPRT = 8+4+4 = 16,但是上面的结果却是12,这是因为派生类并没有生成自己的虚表。派生类真实的内存布局为:
这里写图片描述
调试程序,对Derive类对象d的内存布局进行分析:
这里写图片描述
用上面的打印函数打印验证:
这里写图片描述
这里,编译器是怎么做的呢?
先将基类虚表里的内容拷贝一份,如果派生类对基类中的虚函数进行重写,使用派生类的虚函数替换相同偏移位置的基类虚函数,如果派生类中增加自己的虚函数,按照其在派生类的声明次序,放在上述虚函数之后。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值