知其所以然,C++系列2 - C++对象模型再探

1. 前情回顾:

上文探测了单个含虚函数的类的对象模型,本文继续研究有继承时的对象模型。

2. 有虚函数单继承例子

/*zhaimin.cn@gmail.com*/
#include<iostream>
#include<typeinfo>
using namespace std;
/*********************************************
                       |type_info*
&Derive --> |vptr| --> |print  | --> def of print
            |i   |     |print3 | --> def of print3
            |j   |     |print2 | --> def of print2
            |k   |     |print4 | --> def of print4
***********************************************/

class Base{
public:
        int i;
        int j;
        virtual void print(){cout<<"I am print. addr of i= "<<hex<<&i<<endl;}
        virtual void print3(){cout<<"I am print3. i="<<i<<endl;}
        virtual void print2(){cout<<"I am print2"<<endl;}
        Base(int _i, int _j):i(_i),j(_j){}
};
class Derive:public Base{
public:
        int k;
        virtual void print4(){cout<<"I am print4. k="<<k<<endl;} //newly added
        virtual void print3(){cout<<"I am print3. k="<<k<<endl;}
        Derive(int _i, int _j, int _k):Base(_i,_j),k(_k){}
};
int main(){
        Derive derive(1,2,3);

        cout<<"offset of i: "<<(char*)&(derive.i)-(char*)&derive<<endl; //offset of i: 8
        cout<<"offset of j: "<<(char*)&(derive.j)-(char*)&derive<<endl; //offset of j: 12
        cout<<"offset of k: "<<(char*)&(derive.k)-(char*)&derive<<endl; //offset of k: 16

        void** vtable = *(void***)&derive;
        typedef void (*Func)(void*);
        Func f1 = (Func)vtable[0];
        f1(&derive); //I am print
        Func f2 = (Func)vtable[1];
        f2(&derive); //I am print3. k=3
        Func f3 = (Func)vtable[2];
        f3(&derive); //I am print2
        Func f4 = (Func)vtable[3];
        f4(&derive); //I am print4. k=3

        type_info* ti = (type_info*)vtable[-1];
        cout<<"type_info.name()="<<ti->name()<<endl; //6Derive
        cout<<"real name="<<typeid(derive).name()<<endl;

}
                                                           
offset of i: 8
offset of j: 12
offset of k: 16
I am print. addr of i= 0x7ffc79c7a578
I am print3. k=3
I am print2
I am print4. k=3
type_info.name()=6Derive
real name=6Derive

可见单继承情况下, 子类的内存布局没有太多变化,仅仅是把父类的数据成员放到自己的数据成员前面,其虚函数表也是先把父类的放前面然后是自个的。

/*********************************************
                               |type_info*
&Derive --> |vptr       |  --> |父类虚函数| 
            |父类数据成员|      |子类虚函数| 
            |子类数据成员|      
***********************************************/

3. 多继承情况

/*zhaimin.cn@gmail.com*/
#include<iostream>
#include<typeinfo>
using namespace std;
class Base1{
        public:
                int i;
                int j;
                virtual void print1(){cout<<"I am print1 in Base1."<<endl;}
                virtual void print2(){cout<<"I am print2 in Base1."<<endl;}
                Base1(int _i, int _j):i(_i),j(_j){}
};
class Base2{
        public:
                int k;
                int m;
                virtual void print3(){cout<<"I am print3 in Base2."<<endl;}
                virtual void print4(){cout<<"I am print4 in Base2."<<endl;}
                Base2(int _k, int _m):k(_k),m(_m){}
};
class Derive:public Base1, public Base2{
        public:
                int data;
                virtual void print1(){cout<<"I am print1 in Derive."<<endl;}
                virtual void print3(){cout<<"I am print3 in Derive."<<endl;}
                virtual void print5(){cout<<"I am print5 in Derive."<<endl;}
                Derive(int _i, int _j, int _k, int _m, int _data):Base1(_i,_j),Base2(_k,_m),data(_data){}
};

int main(){
        Derive derive(1,2,3,4,5);
        cout<<"offset of Base1::i: "<<offsetof(Derive,i)<<endl;
        cout<<"offset of Base1::j: "<<offsetof(Derive,j)<<endl;
        cout<<"offset of Base2::k: "<<offsetof(Derive,k)<<endl;
        cout<<"offset of Base2::m: "<<offsetof(Derive,m)<<endl;
        cout<<"offset of Derive::data: "<<offsetof(Derive,data)<<endl;

        void** vtable = *(void***)&derive;
        typedef void (*Func)(void*);
        for(int i=0;i<5;i++){
                Func f = (Func)vtable[i];
                cout<<"addr of this function("<<i<<"): "<<hex<<(long)f<<endl;
                f(&derive);
        }

        type_info* ti = (type_info*)vtable[-1];
        cout<<"type_info.name()="<<ti->name()<<endl; //6Derive
        cout<<"real name="<<typeid(derive).name()<<endl;
}

在没有实验之前,你可能猜测多继承也会和单继承一样累加数据成员和累加虚函数而已,而上面例子的真实结果是崩溃了!

offset of Base1::i: 8
offset of Base1::j: 12
offset of Base2::k: 24
offset of Base2::m: 28
offset of Derive::data: 32
addr of this function(0): 400f1c
I am print1 in Derive.
addr of this function(1): 400e34
I am print2 in Base1.
addr of this function(2): 400f48
I am print3 in Derive.
addr of this function(3): 400f7a
I am print5 in Derive.
addr of this function(4): fffffffffffffff0
Segmentation fault (core dumped)

不过从打印结果可以看出,似乎k前面还有8个字节的额外数据,难不成是另外一个虚函数表??让我们验证一下。

/*zhaimin.cn@gmail.com*/
#include<iostream>
#include<typeinfo>
using namespace std;
class Base1{
        public:
                int i;
                int j;
                virtual void print1(){cout<<"I am print1 in Base1."<<endl;}
                virtual void print2(){cout<<"I am print2 in Base1."<<endl;}
                Base1(int _i, int _j):i(_i),j(_j){}
};
class Base2{
        public:
                int k;
                int m;
                virtual void print3(){cout<<"I am print3 in Base2."<<endl;}
                virtual void print4(){cout<<"I am print4 in Base2."<<endl;}
                Base2(int _k, int _m):k(_k),m(_m){}
};
class Derive:public Base1, public Base2{
        public:
                int data;
                virtual void print1(){cout<<"I am print1 in Derive."<<endl;}
                virtual void print3(){cout<<"I am print3 in Derive."<<endl;}
                virtual void print5(){cout<<"I am print5 in Derive."<<endl;}
                Derive(int _i, int _j, int _k, int _m, int _data):Base1(_i,_j),Base2(_k,_m),data(_data){}
};

int main(){
        Derive derive(1,2,3,4,5);
        cout<<"offset of Base1::i: "<<offsetof(Derive,i)<<endl;
        cout<<"offset of Base1::j: "<<offsetof(Derive,j)<<endl;
        cout<<"offset of Base2::k: "<<offsetof(Derive,k)<<endl;
        cout<<"offset of Base2::m: "<<offsetof(Derive,m)<<endl;
        cout<<"offset of Derive::data: "<<offsetof(Derive,data)<<endl;

        typedef void (*Func)(void*);
        void** vtable1 = *(void***)&derive;
        cout<<"--------first vtable functions----------"<<endl;
        for(int i=0;i<4;i++){
                Func f = (Func)vtable1[i];
                long lf = (long)f;
                cout<<"addr of this function("<<i<<"): "<<hex<<(long)f<<endl;
                if(lf==0)break;
                f(&derive);
        }
        type_info* ti1 = (type_info*)vtable1[-1];
        cout<<"type_info.name()="<<ti1->name()<<endl; //6Derive


        cout<<"--------next vtable functions----------"<<endl;
        void** vtable2 = *(void***)((char*)&derive+offsetof(Derive,k)-8);
        for(int i=0;i<3;i++){
                Func f = (Func)vtable2[i];
                long lf = (long)f;
                cout<<"addr of this function("<<i<<"): "<<hex<<lf<<endl;
                if(lf==0)break;
                f(&derive);
        }
        type_info* ti2 = (type_info*)vtable2[-1];
        cout<<"type_info.name()="<<ti2->name()<<endl; //6Derive
}
offset of Base1::i: 8
offset of Base1::j: 12
offset of Base2::k: 24
offset of Base2::m: 28
offset of Derive::data: 32
--------first vtable functions----------
addr of this function(0): 401108
I am print1 in Derive.
addr of this function(1): 401020
I am print2 in Base1.
addr of this function(2): 401134
I am print3 in Derive.
addr of this function(3): 401166
I am print5 in Derive.
type_info.name()=6Derive
--------next vtable functions----------
addr of this function(0): 40115f
I am print3 in Derive.
addr of this function(1): 4010aa
I am print4 in Base2.
addr of this function(2): 0
type_info.name()=6Derive

果然有两个虚函数表,布局如下:

/*********************************************
&Derive --> |vptr1        | --> |print1/2/3/5| 前面有一个指针指向type_info of Derive
            |Base1 members|
            |vptr2        | --> |print3/4|   前面有一个指针指向type_info of Derive
            |Base2 members|
***********************************************/

1. 第一个虚函数表包括Base1+Derive中的虚函数,声明相同的会被子类覆盖。

2. 第二个虚函数表只包括自身的虚函数,但会被子类覆盖(如子类有)。

print1/3/5已经被覆盖成Derive的了,这就是多态的本质:虚函数在编译期间已经被覆盖成最新的了。

4. 问题

sizeof(Derive) 是多少哪?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深山老宅

鸡蛋不错的话,要不要激励下母鸡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值