标签:
c++继承和多态:
1.普通继承派生类对象的内存布局
class Base
{
public:
void show() const
{
cout << "Base::show()" << endl;
}
protected:
int ma;
};
class Derive: public Base
{
public:
void show() const
{
cout << "Derive::show()" << endl;
}
private:
int mb;
};
在没有虚函数时,派生类对象内存分布如图
Derive::
Base::
ma
mb
这两个show()函数的关系是隐藏
-------------------------------------------------------
若基类存在虚函数:两个show()函数是覆盖
class Base
{
public:
virtual void show() const
{
cout << "Base::show()" << endl;
}
protected:
int ma;
};
class Derive: public Base
{
public:
void show() const
{
cout << "Derive::show()" << endl;
}
private:
int mb;
};
此时派生类的内存布局:
Derive::
Base::
*vfptr
ma
mb
虚表指针指向一个虚表,虚表内容如下
Base::vftable
-2: RIIT //运行的类型信息,它提供了运行时确定对象类型的方法。 使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast确定对象的动态类型
-1: vfptr在内存布局的偏移量
0: show 虚函数的入口地址
如果virtual加在派生类的show上,则派生类的内存布局
Derive::
vfptr
Base::
ma
mb
----------------------------------------------------------
编译器是在构造函数创建这个虚表指针以及虚表的。
那么编译器是如何利用虚表指针与虚表来实现多态的呢?是这样的,当创建一个含有虚函数的父类的对象时,编译器在对象构造时将虚表指针指向父类的虚函数;同样,当创建子类的对象时,编译器在构造函数里将虚表指针(子类只有一个虚表指针,它来自父类)指向子类的虚表(这个虚表里面的虚函数入口地址是子类的)。
所以,如果是调用Base *p = new Derived();生成的是子类的对象,在构造时,子类对象的虚指针指向的是子类的虚表,接着由Derived*到Base*的转换并没有改变虚表指针,所以这时候p->show,实际上是p->vfptr->show,它在构造的时候就已经指向了子类虚函数,所以调用的是子类的虚函数,这就是多态了。
2.虚继承下派生类的内存布局
///虚拟继承是为了解决多重继承下公共基类的多份拷贝问题.
class Base
{
public:
virtual void show() const
{
cout << "Base::show()" << endl;
}
};
class D11 : virtual public Base
{
public:
int mb;
};
class D12: virtual public Base
{
publilc:
int mc;
};
class D21:public D11, public D12
{
int md;
};
D21的对象内存布局
////////////////////////////////////////////////////////////////////////
D21::
D11::
vbptr
mb
D12::
vbptr
mb
md
Base::
vfptr
ma
////////////////////////////////////////////////////////////////////////
其中虚基类指针vbptr指向vbtable
vbtable::
0 : vbptr在他当前类的内存偏移量
1: vbptr到虚基类数据的偏移量
标签:
原文地址:http://blog.csdn.net/it_lover_/article/details/51334552