类的大小与数据成员有关与成员函数无关
类的大小与静态数据成员无关
虚继承对类的大小的影响
虚函数对类的大小的影响
只出现虚继承的情况
virtual base table
本类地址 与虚基类表指针地址的差
虚基类地址与虚基类表指针地址的差
virtual base table pointer(vbptr)
#include <iostream>
using namespace std;
class BB
{
public :
int bb_ ;
};
class B1 : virtual public BB
{
public :
int b1_ ;
};
class B2 : virtual public BB
{
public :
int b2_ ;
};
class DD : public B1, public B2
{
public :
int dd_ ;
};
int main (void)
{
cout<<sizeof (BB)<< endl;
cout<<sizeof (B1)<< endl;
cout<<sizeof (DD)<< endl;
B1 b1 ;
int** p ;
cout<<&b1 <<endl;
cout<<&b1 .bb_<< endl;
cout<<&b1 .b1_<< endl;
p = (int **)&b1;
cout<<p [0][0]<<endl;
cout<<p [0][1]<<endl;
DD dd ;
cout<<&dd <<endl;
cout<<&dd .bb_<< endl;
cout<<&dd .b1_<< endl;
cout<<&dd .b2_<< endl;
cout<<&dd .dd_<< endl;
p = (int **)ⅆ
cout<<p [0][0]<<endl;
cout<<p [0][1]<<endl;
cout<<endl ;
cout<<p [2][0]<<endl;
cout<<p [2][1]<<endl;
BB* pp ;
pp = &dd ;
dd.bb_ = 10; //对象的内存模型在编译时就已经确定了,否则无法定义类的对象,因为要开辟内存
int base = pp-> bb_; // 通过间接访问 (其实pp 已经偏移了20 ),这需要运行时的支持
cout<<"dd.bb_=" <<base<< endl;
return 0;
}
对象内存模型图
每一个虚继承对象,都有一个虚基类表指针。对象的内存模型在编译时就已经确定了,否则无法定义类的对象,因为要开辟内存。
只出现虚函数的情况
#include <iostream>
using namespace std;
class BB
{
public :
int bb_ ;
};
class B1 : virtual public BB
{
public :
int b1_ ;
};
class B2 : virtual public BB
{
public :
int b2_ ;
};
class DD : public B1, public B2
{
public :
int dd_ ;
};
int main (void)
{
cout<<sizeof (BB)<< endl;
cout<<sizeof (B1)<< endl;
cout<<sizeof (DD)<< endl;
B1 b1 ;
int** p ;
cout<<&b1 <<endl;
cout<<&b1 .bb_<< endl;
cout<<&b1 .b1_<< endl;
p = (int **)&b1;
cout<<p [0][0]<<endl;
cout<<p [0][1]<<endl;
DD dd ;
cout<<&dd <<endl;
cout<<&dd .bb_<< endl;
cout<<&dd .b1_<< endl;
cout<<&dd .b2_<< endl;
cout<<&dd .dd_<< endl;
p = (int **)ⅆ
cout<<p [0][0]<<endl;
cout<<p [0][1]<<endl;
cout<<endl ;
cout<<p [2][0]<<endl;
cout<<p [2][1]<<endl;
BB* pp ;
pp = &dd ;
dd.bb_ = 10; //对象的内存模型在编译时就已经确定了,否则无法定义类的对象,因为要开辟内存
int base = pp-> bb_; // 通过间接访问 (其实pp 已经偏移了20 ),这需要运行时的支持
cout<<"dd.bb_=" <<base<< endl;
return 0;
}
对象内存模型图
继承类对象,先构造父类,再构造子类。
砖石继承
#include <iostream>
using namespace std;
class BB
{
public:
virtual void vfbb()
{
cout << "BB:vfbb().." << endl;
}
int bb_;
};
class B1 : public BB
{
public:
virtual void vfb1()
{
cout << "B1:vfb1().." << endl;
}
int b1_;
};
class B2 : public BB
{
public:
virtual void vfb2()
{
cout << "B2:vfb2().." << endl;
}
int b2_;
};
class DD : public B1, public B2
{
public:
virtual void vfdd()
{
cout << "DD:vfdd().." << endl;
}
int dd_;
};
typedef void (* FUNC)(void );
int main()
{
cout << sizeof(BB) << endl;
cout << sizeof(B1) << endl;
cout << sizeof(DD) << endl;
cout << endl;
DD dd ;
cout << &dd << endl;
cout << &dd.B1::bb_ << endl;
cout << &dd.B2::bb_ << endl;
cout << &dd .b1_ << endl;
cout << &dd .b2_ << endl;
cout << &dd .dd_ << endl;
cout << endl;
B1 b ;
int **p = (int **)& b;
FUNC fun = (FUNC) p[0][0];
fun();
fun = (FUNC )p[0][1];
fun();
cout << endl ;
p = (int **)&dd
fun = (FUNC)p[0][0];
fun();
fun = (FUNC)p[0][1];
fun();
fun = (FUNC)p[0][2];
fun();
fun = (FUNC)p[3][0];
fun();
fun = (FUNC)p[3][1];
fun();
cout << endl;
return 0;
}
对象内存模型图
DD::vfdd的位置跟继承的顺序有关,如果DD先继承的是B2, 那么它将跟在B2::vfb2的下面。
如果派生类是从多个基类继承或者有多个继承分支(从所有根类开始算起),而其中若干个继承分支上出现了多态类,则派生类将从这些分支中的每个分支上继承一个vptr,编译器也将为它生成多个vtable,有几个vptr就生成几个vtable(每个vptr分别指向其中一个),分别与它的多态基类对应。
虚继承与虚函数同时出现
#include <iostream>
using namespace std;
class BB
{
public :
virtual void vfbb()
{
cout<<"BB::vfbb" <<endl;
}
virtual void vfbb2()
{
cout<<"BB::vfbb2" <<endl;
}
int bb_ ;
};
class B1 : virtual public BB
{
public :
virtual void vfb1()
{
cout<<"B1::vfb1" <<endl;
}
int b1_ ;
};
class B2 : virtual public BB
{
public :
virtual void vfb2()
{
cout<<"B2::vfb2" <<endl;
}
int b2_ ;
};
class DD : public B1, public B2
{
public :
virtual void vfdd()
{
cout<<"DD::vfdd" <<endl;
}
int dd_ ;
};
typedef void (* FUNC)(void);
int main (void)
{
cout<<sizeof (BB)<< endl;
cout<<sizeof (B1)<< endl;
cout<<sizeof (DD)<< endl;
BB bb ;
int** p ;
p = (int **)&bb;
FUNC fun ;
fun = (FUNC )p[0][0];
fun();
fun = (FUNC )p[0][1];
fun();
cout<<endl ;
B1 b1 ;
p = (int **)&b1;
fun = (FUNC )p[0][0];
fun();
fun = (FUNC )p[3][0];
fun();
fun = (FUNC )p[3][1];
fun();
cout<<p [1][0]<<endl;
cout<<p [1][1]<<endl;
cout<<endl ;
DD dd ;
p = (int **)ⅆ
fun = (FUNC )p[0][0];
fun();
fun = (FUNC )p[0][1]; // DD::vfdd 挂在 B1::vfb1的下面
fun();
fun = (FUNC )p[3][0];
fun();
fun = (FUNC )p[7][0];
fun();
fun = (FUNC )p[7][1];
fun();
cout<<p [1][0]<<endl;
cout<<p [1][1]<<endl;
cout<<p [4][0]<<endl;
cout<<p [4][1]<<endl;
return 0;
}
对象内存模型图
如果没有虚继承,则虚函数表会合并,一个类只会存在一个虚函数表和一个虚函数表指针,同个类的对象共享,当然也不会有虚基类表和虚基类表指针的存在。但如果是钻石继承,那么是会存在两份虚函数表和两份虚函数表指针的。