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) 是多少哪?