虚函数表及虚函数表指针(看似简单,深入部分也不是很懂)

回顾一下以前对虚函数表及虚表指针的概念:

1. 虚函数表属于类,同类对象间共享该虚函数表(貌似虚函数表里面维护了一个函数地址的指针数组)。

2.不同对象各自维护一个虚表指针指向类的虚表,类对象大小包含成员变量大小(含虚表指针vptr大小) ——(计算大小要考虑字节对齐 32位默认4字节对齐,64位8字节)。

3.虚表指针在对象内存的开始位置。

4.虚函数调用时,动态联编的多态特性及运行时判断执行哪个函数,所以虚函数执行效率比普通函数要低。

5.子类继承父类后,如果子类有父类的重名虚函数,则会覆盖(重写)父类的虚函数,虚表内函数指针也会替换成子类的。

 

 

class Base{
  virtual void print(){}
};

class Base2{
  virtual void dprint(){}
};

class CBase: public Base, public Base2{

};

//g++  align.cpp -fdump-class-hierarchy

-------------------------------------linux 64位系统------------------------------------------------

Vtable for Base    //虚函数表内存储了虚函数的地址
Base::_ZTV4Base: 3u entries    //虚表是属于类,而不是属于某个具体的对象,一个类只需要一个虚表
0     (int (*)(...))0            //一个空的变长参数函数指针??
8     (int (*)(...))(& _ZTI4Base)  //一个指向虚函数表的指针 = 把虚函数表地址转成变长参数的函数指针?
16    Base::print

//24 xxx

Class Base
   size=8 align=8
   base size=8 base align=8
Base (0x7f1f07d2bd90) 0 nearly-empty
    vptr=((& Base::_ZTV4Base) + 16u) //可以看出虚函数表首地址还有两个函数指针,偏移16字节后是第一个虚函数的地址

//虚函数表指针vptr指向的是第一个虚函数,而不是虚函数表首地址

//同理,32位系统指针4字节,则应该是虚函数表指针=虚函数表首地址 + 8字节偏移 = 第一个虚函数地址

Vtable for Base2
Base2::_ZTV5Base2: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5Base2)
16    Base2::dprint

Class Base2
   size=8 align=8
   base size=8 base align=8
Base2 (0x7f1f07d2bf50) 0 nearly-empty        //每个类的vptr放在类内存空间的起始位置
    vptr=((& Base2::_ZTV5Base2) + 16u)

Vtable for CBase
CBase::_ZTV5CBase: 6u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5CBase)
16    Base::print
24    (int (*)(...))-0x00000000000000008
32    (int (*)(...))(& _ZTI5CBase)
40    Base2::dprint

Class CBase   //C++多重继承中,会顺序存储所有基类的虚函数表指针(即有多个虚表指针)
   size=16 align=8   

   base size=16 base align=8
CBase (0x7f1f07d91380) 0
    vptr=((& CBase::_ZTV5CBase) + 16u)    //子类复用第一个基类的虚表指针,并对虚表进行覆盖或者添加
  Base (0x7f1f07d96070) 0 nearly-empty
      primary-for CBase (0x7f1f07d91380)  //第一个基类会用于初始化子类(我认为是初始化子类虚表),然后子类对象的虚表指针指向这个子类虚表。
  Base2 (0x7f1f07d960e0) 8 nearly-empty
      vptr=((& CBase::_ZTV5CBase) + 40u)  //子类对象第二个虚表指针,指向子类虚表中来自第二个基类的第一个虚函数地址

___________________________________________________________

class Base{
  virtual void print(){}
};

class CBase: public Base{

};
 

Vtable for Base
Base::_ZTV4Base: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI4Base)
16    Base::print

Class Base
   size=8 align=8
   base size=8 base align=8
Base (0x7f1eeb8e9d90) 0 nearly-empty
    vptr=((& Base::_ZTV4Base) + 16u)

Vtable for CBase
CBase::_ZTV5CBase: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5CBase)
16    Base::print

Class CBase
   size=8 align=8
   base size=8 base align=8
CBase (0x7f1eeb954070) 0 nearly-empty
    vptr=((& CBase::_ZTV5CBase) + 16u)
  Base (0x7f1eeb9540e0) 0 nearly-empty
      primary-for CBase (0x7f1eeb954070)

Class Base2
   size=8 align=8
   base size=8 base align=8
Base2 (0x7f16e5f4ea10) 0 nearly-empty
    vptr=((& Base2::_ZTV5Base2) + 16u)

Vtable for CBase
CBase::_ZTV5CBase: 5u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5CBase)
16    Base::print
24    Base::test
32    CBase::ok  //子类虚函数地址会放在基类后面

某大牛如是说:子类中声明的虚函数除了覆盖各个基类对应函数的指针外,还额外添加一份到第一个基类的vptr中(体现了共用的意义)。我表示对共用这个词持保留意见,第一虚表属于各个类,父类和子类不是同一个;第二虚表指针属于对象,各个对象都有。子类复用继承下来的父类声明的虚表指针而已,该指针指向的是子类虚表。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章