zoukankan      html  css  js  c++  java
  • C++ 虚函数机制学习

    致谢


      本文是基于对<Inside the c++ object model>的阅读和gdb的使用而完成的.在此感谢Lippman对cfront中对象模型的解析,这些解析帮助读者拨开迷雾.此外,Linux下无比强大的gdb工具更是驱散"黑暗"的"明灯".  :)

    No-Inheritance


     1 class Base {
     2     public:
     3         int a = 21;
     4         static int b;
     5         int c = 22;
     6 
     7         void showBase1();
     8         static int showBase2();
     9 };
    10 
    11 void Base::showBase1() {
    12     cout<<"Base"<<endl;
    13 }   
    14 int Base::showBase2() {
    15     cout<<"base2"<<endl;
    16 }

    解析:

      使用GDB查看内存空间,显示

        Base中a的地址为0x7fffffffdc50

        Base中c的地址为0x7fffffffdc54

        Base中b的地址为0x601068

        showBase1的地址为0x40085e(参数为Base * const -> this指针为常量指针)

        showBase2的地址为0x400888(参数为void)

      显然

        non-static data member是存储在class object中;

        static data member, member function因为是所有本class的对象所share的,所以放置在了一个公共区域;

    Inheritance without Polymorphism


     1 class Base {
     2     public:
     3         int a = 21;
     4         static int b;
     5         int c = 22;
     6 
     7         void showBase1();
     8 };      
     9 int Base::b = 10;
    10 void Base::showBase1() {
    11     cout<<"Base"<<endl;
    12 }   
    13 
    14 class Inheri : public Base {
    15     public:
    16         int c = 23;
    17         static int d;
    18         
    19         static int showInheri1();
    20 };      
    21 int Inheri::d = 11;
    22 int Inheri::showInheri1(){
    23     cout<<"Inheri"<<endl;
    24 }

    解析:  

      使用GDB查看内存空间,显示

        Inheri中a的地址为0x7fffffffdc70

        Inheri中c的地址为0x7fffffffdc78

        Inheri中b的地址为0x601068

        Inheri中b的地址为0x60106c

        showBase1的地址为0x40085e(参数为Base * const -> this指针为常量指针)

        showInheri1的地址为0x400888(参数为void)

      显然

        derived class object中包含 基类和派生类的non-static data member;

    No-Inheritance with Polymorphism


     1 class Base {
     2     public:
     3         int a = 21;
     4         static int b;
     5         int c = 22;
     6 
     7         virtual void showBase1();//virtual function
     8 };      
     9 int Base::b = 10;
    10 void Base::showBase1() {
    11     cout<<"Base"<<endl;
    12 }

      使用gdb查看base class object( p ptr )

         " {_vptr.Base = 0x400af0 <vtable for Base+16>, a = 21, static b = 10, c = 22} "

            在类中使用虚机制(虚函数,虚基类,虚继承..)时,会为每个object添加vptr来指向所对应的vtbl.

      

      使用gdb查看vptr指向的虚函数(p /a *(void**)0x400af0

        " {0x40092e <Base::showBase1()>, 0x697265686e4936} " 

      

    Single-Inheritance with Virtual Mechanism


    class Base {
        public:
            int a = 21;
            static int b;
            int c = 22;
    
            virtual void showBase1();
    };
    int Base::b = 10;
    void Base::showBase1() {
        cout<<"Base"<<endl;
    }
    
    class Inheri : public Base {
        public:
            int c = 23;
            static int d;
    
            static int showInheri1();
    };
    int Inheri::d = 11;
    int Inheri::showInheri1(){
        cout<<"Inheri"<<endl;
    }

      查看Inheri class object

        {<Base> = {_vptr.Base = 0x400bc0 <vtable for Inheri+16>, a = 21, static b = 10, c = 22}, c = 23, static d = 11}

      查看Ineri class object 的vtbl

        0x400998 <Inheri::show()>

      可以看到derived class object直接使用了从base class subobject中继承而来的vptr.

      同样,查看Base class object

        {_vptr.Base = 0x400be0 <vtable for Base+16>, a = 21, static b = 10, c = 22}

      查看Base class object的vtbl

        0x40096e <Base::show()>         

      

      从这里可以看出来,在Single Inheritance中每个class object的vtbl中都只包含本class所对应的virtual function.

      

      我们再测试一下derived class赋值给base class pointer的情况.

        Base* bbptr = new Inheri;

      查看bbptr所指向的内存:

        {<Base> = {_vptr.Base = 0x400bc0 <vtable for Inheri+16>, a = 21, static b = 10, c = 22}, c = 23, static d = 11}

      查看vtbl中的内容:

        0x400998 <Inheri::show()>

      这里我们可以有两点发现:

        1) 虽然使用的是基类指针Base* 来接Inheri对象,但是其vptr所指向的vtbl仍然是Inheri class的;

        2) 每个class所对应的vtbl在内存中只有一份,在测试中bbptr和iptr指向的vtbl都是位于0x400bc0

    Multiple-Inheritance with Virtual Mechanism


        

     1 class Base {
     2     public:
     3         int a = 21;
     4         static int b;
     5         int c = 22;
     6 
     7         virtual void show();//inline
     8 };
     9 int Base::b = 10;
    10 void Base::show() {
    11     cout<<"Base"<<endl;
    12 }
    13 
    14 class Inheri : public Base {
    15     public:
    16         int c = 23;
    17         static int d;
    18 
    19          virtual void show();
    20 };
    21 int Inheri::d = 11;
    22 void Inheri::show(){
    23     cout<<"Inheri"<<endl;
    24 }
    25 
    26 class OtherBase{
    27     public:
    28         int oa;
    29         virtual void show();
    30 };
    31 void OtherBase::show(){
    32     cout<<"OtherBase"<<endl;
    33 }
    34 
    35 class Final : public OtherBase,public Inheri{
    36     public:
    37         virtual void show();
    38 };                                                                                                                                                                                             
    39 void Final::show() {
    40     cout<<"Final"<<endl;
    41 }

      查看Final对象

        "{<OtherBase> = {_vptr.OtherBase = 0x400cd0 <vtable for Final+16>, oa = 0}, <Inheri> = {<Base> = {_vptr.Base = 0x400ce8 <vtable for Final+40>, a = 21, static b = 10, c = 22}, c = 23, static d = 11}, <No data fields>}"

        可以观察到:

          1) 子对象从右向左的被构建

          2) 分别包含OtherBase和Base的vptr,这是为了在derived class object 赋予base class object时更容易处理.

         继续查看OtherBase和Base中vptr的信息

        _vptr.OtherBase所指向的vtbl中信息为 : 0x400a5c <Final::show()>

          _vptr.Base所指项的vtbl中信息为 : 0x400a86 <_ZThn16_N5Final4showEv>

        可以看到这两个vtbl所保存的都是Final::show.因此, 通过Final来为各个基类指针赋值时,最后总是调用Final自身的虚函数. 

    Virtual Inheritance


      

     1 class _ios {
     2     public:
     3         int i;
     4         virtual void show();
     5 };
     6 void _ios::show() {
     7     cout<<"ios"<<endl;
     8 }
     9 
    10 
    11 class _istream : public _ios {
    12     public:
    13         int is; 
    14         virtual void show();
    15 };
    16 void _istream::show() {
    17     cout<<"istream"<<endl;
    18 }
    19 
    20 class _ostream : public _ios {
    21     public:
    22         int os; 
    23         virtual void show();
    24 };
    25 void _ostream::show() {
    26     cout<<"ostream"<<endl;
    27 }
    28 
    29 class _iostream : public _istream, public _ostream {
    30     public:
    31         int ios;
    32         virtual void show();
    33 };
    34 void _iostream::show() {
    35     cout<<"iostream"<<endl;
    36 }

      在未使用virtual inheritance时, 查看 _iostream 对象, 会看到两份 _ios 类的对象,分别属于_istream和_ostream : 

        "{<_istream> = {<_ios> = {_vptr._ios = 0x400c90 <vtable for _iostream+16>, i = 0}, is = 0}, <_ostream> = {<_ios> = {

    _vptr._ios = 0x400ca8 <vtable for _iostream+40>, i = 0}, os = 0}, ios = 0}"

        符合之前介绍的single inheritance with polymorphism, _istream和_ostream分别使用从_ios中而来的vptr.ios来指向自己的vtbl. 

      使用virtual inheritance时 ,可以看到只有一份 _ios对象: 

        "{<_istream> = {<_ios> = {_vptr._ios = 0x400d18 <vtable for _iostream+88>, i = 0}, _vptr._istream = 0x400cd8 <vtable for _iostream+24>, is = 0}, <_ostream> = {_vptr._ostream = 0x400cf8 <vtable for _iostream+56>, os = 0}, ios = 0}"

        在虚继承中,没有和单一继承中那样继承基类的vptr, 而是拥有自己的vptr.

          继续查看vtbl中内容,分别显示 : 

        0x400a40 <_ZTv0_n24_N9_iostream4showEv>

        0x400a3a <_ZThn16_N9_iostream4showEv>

        可见, 虚函数表中的函数也都是_iostream class中的member function.因此, 无论_iostream对象赋值给那个base class subobject的指针,总能调用到_iostream class的virtual function.

    Reference


      <Inside the C++ Object Model>

      <GDB Manul>

    备注


      更多内容详见 https://github.com/CarlSama/Inside-The-CPP-Object-Model-Reading-Notes

  • 相关阅读:
    根据模板自动生成数据
    CSV to XLSX (专用)
    释放用完的Excel COM组件
    配置文件的力量
    字符编解码的故事(ASCII,ANSI,Unicode,Utf-8区别)
    将结果中的省略号内容全部输出
    Powershell变量的类型
    一些用过的C#类库收集
    运算符
    特殊运算符
  • 原文地址:https://www.cnblogs.com/carlsama/p/4401756.html
Copyright © 2011-2022 走看看