C++虚继承

 

什么是虚继承

子类在继承父类时,在声明前加virtual关键字,这就是虚继承
例:

class A
{
  //......
}

class B:virtual public A
{
  //.......
}

什么时候需要使用虚继承

在多重继承时,正常情况下子类对象将拥有继承链上所有父类对应的部分,但是如果两个父类都继承了同一个类,
那么在子类将拥有两份祖父类的数据,比如菱形继承:
20190804190545.png

 
下面我以四个简单类来分析其内存结构:

class CFuniture
{
  int m_nFun;
public:
  CFuniture()
  {
    m_nFun = 0;
  }

  virtual ~CFuniture()
  {

  }

  virtual void FunitureName()
  {
    cout << "CFuniture::FunitureName()" << endl;
  }
};


class CSofa : public CFuniture
{
  int m_nSofa;
public:

  virtual void Sit()
  {
    cout << "CSofa::Sit()" << endl;
  }

  CSofa()
  {
    m_nSofa = 1;
  }

  virtual ~CSofa()
  {
    cout << "~CSofa()" << endl;
  }
};


class CBed : public CFuniture
{
  int m_nBed;
public:
  CBed()
  {
    m_nBed = 2;
  }

  virtual void Sleep()
  {
    cout << "CBed::Sleep()" << endl;
  }

  virtual ~CBed()
  {

  }
};

class CSofaBed :public CSofa, public CBed
{
  int m_nSofaBed;
public:
  CSofaBed()
  {
    m_nSofaBed = 3;
  }

  virtual ~CSofaBed()
  {
    cout << "~CSofaBed()" << endl;
  }

  virtual void SleepAndSit()
  {
    cout << "CSofaBed::SleepAndSit()" << endl;
  }

  virtual void SitAndSleep()
  {
    cout << "CSofaBed::SitAndSleep()" << endl;
  }

  virtual void Sleep()
  {
    cout << "CSofaBed::Sleep()" << endl;
  }

  virtual void Sit()
  {
    cout << "CSofaBed::Sit()" << endl;
  }
};


int main(int argc, char **argv)
{
  CSofaBed sofabed;
  return 0;
}

这里家具类CFuniture祖父类,沙发类CSofa和床类CBed都继承继承自CFuniture,沙发床类CSofaBed同时继承
了CSofa和CBed,现在来看看CSofaBed对象的内存布局:
20190805094512.png
可以看出祖父类CFuniture在CSofaBed中有两份,这并非我们本意,我只想CSofaBed类对象中只有一份CFuniture
类数据,那么可以在CSofa和CBed继承CFuniture时,加上virtual关键字表示和其它类共享CFuniture:

class CSofa :virtual public CFuniture
class CBed :virtual public CFuniture

此时再来看看CSofaBed对象的内存布局:
20190805095742.png
从监视窗口可以看出CFunitrue类的数据有独立的一部分,但是CSofa和CBed中依旧有CFunitrue类的数据,
其实这是VS的监视窗口为了更直观的显示继承体系,在CSofa和CBed中显示了CFunitrue类的数据,但是从
内存窗口中可以看出CFunitrue类的数据只有一份;

 
从上图可以看出CFunitrue类在CSofaBed子类中被独立出来,放在了对象的最尾部内存地址处,因为在CSofa和CBed以及CFunitrue中都有自己的虚函数,所以CSofaBed子类此时拥有三张属于自己虚表(和父类中的虚表不是同一个),另外CSofaBed子类自己新加的虚函数被追加到继承自CSofa虚表的后面,CSofaBed的析构函数被放到
继承自祖父类CFunitrue的虚表中

 
还可以从内存布局中看出CSofaBed的两个直接基类的虚表指针后面,还跟着两个指针:0x01259b68和0x01259bbc
这两个指针是偏移指针,它们两指向相同的结构体,该结构体共8字节,前四个字节无意义,后四个字节记录了偏移
指针距离CFunitrue类部分的字节数;从上图中可以看出sofbed对象中,CFunitrue类的部分位于0x0044FBDC
开始的,CSofa类部分的偏移指针在0x0044FBC0处,两者之间间隔了0x18个字节;CBed类部分的偏移指针位于0x0044FBD0处,和CFunitrue类的部分间隔了0x0C个字节

 

posted @ 2019-08-05 10:11  CodeMaker+  阅读(815)  评论(0编辑  收藏  举报