none
I don't know how the vbtable works. RRS feed

  • 질문

  •  

     

     

     

    class Z
    {
       private:
       int z1;
       int z2;
       public:
       Z(int num1, int num2):z1(num1),z2(num2)
       {}
    };

    class A
    {
    private:
     int a1;
     int a2;
    public:
         A(int num1, int num2):a1(num1),a2(num2)
         {}
         void functest()
         {
                cout<<"모호한 함수"<<endl;
         }
    };


    class B : virtual public A, virtual public Z
    {
    private:
     int b;
    public:
             B(int num1):b(num1), A(1,2), Z(1,2)
            {
            }
            void funcb(){
                    cout<<"funcb"<<endl;
             }
    };

    class C : virtual public A, virtual public Z
    {
    private:
     int c;
    public:
             C(int num1):c(num1),A(1,2), Z(1,2)
             {
             }
             void funcc(){
                    cout<<"funcc"<<endl;
             }
    };

    class D : virtual public A, virtual public Z
    {
    private:
     int d;
    public:
              D(int num1):d(num1),A(1,2), Z(1,2)
             {
             }
             void funcd(){
                  cout<<"funcd"<<endl;
             }
    };

    class E : public B, public C, public D
    {
    private:
     int e;
    public:
             E():A(1,2),Z(3,4),C(1), B(3), D(6), e(8) {}
             void funce(){
                    functest();   // we are in this context;
                    funcd();
                    cout<<"funce"<<endl;
              }
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
              B b(4);
              E e;
              e.funce();

              return 0;
    }

     

     

    In the visual C++ 2010, I was going to know C++ Internal structure about Virtual-multiple inheritance.

    I found the vbtables, I saw the inputing the table in the object memory stack, but I coudn't find using of the table.

    E:

    func()

      functest();
    00141863 8B 45 F8             mov         eax,dword ptr [this]  //eax=this
    00141866 8B 08                mov         ecx,dword ptr [eax]    //ecx=(dword)*this=E::vbtable
    00141868 8B 55 F8             mov         edx,dword ptr [this]  //edx=this
    0014186B 03 51 04             add         edx,dword ptr [ecx+4] //edx=edx+*(dword)[vbtable+4]=this+1c(A offset)
    0014186E 8B CA                mov         ecx,edx                       //ecx=edx  //I had thought that this information have to be used in next process(in A::functest)
    00141870 E8 1F FA FF FF       call        A::functest (141294h) 

     

    this is execution before starting the most top base class's function(A::functest)

    at the end, ecx contains the address of A object, in this process, the compiler used a vbtable (00141866 8B 08 mov ecx,dword ptr [eax], //eax=this=vbtable ), but in the A::functest, the compiler didn't use the information about the address of A object.

    So, I can't understand what the address(vbtable or A address) means.

    Is this the virtual-inheritance optimization?

    Could you please show me the answer?



    • 편집됨 yangil06 2011년 10월 3일 월요일 오전 6:52
    2011년 10월 3일 월요일 오전 6:47

모든 응답

  • Hi,

    Thanks for posting here.

    I have researched your question, but  I got nothing.

    Wait for me to obtain correct answer , I am going to try to research your question again.

    Regards,

    Forum Support

    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.

    2011년 10월 7일 금요일 오전 12:20
    중재자
  • Hi,

    Thanks for posting here.

     

    1.       Is this the virtual-inheritance optimization?

    I think that there is no releated thing about virtual-inheritance optimization.

     

    Virtual base table

    This samples have no virtual function.

    Virtual function use virtual function table so that calling function use indirect method.

    But Virtual Inheritance is that compiler decide to use parent class instance which E class derived from, during construct time.

    calling function of parent class don’t need to use virtual function table. but E Constructor use virtual base table at construct time

    it is point that using virtual base table raise at construct time, but using virtual function table raise before calling function.

    Partial of above disassembly that is in before calling function of A::functest don’t show using virtual base table.

     

    Below Figure of  Partial disassembly of E Construct.

    Regards,

    Forum Support

    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.

    2011년 10월 7일 금요일 오전 2:24
    중재자
  • I've read your answer. Thanks for your reply. But I can't still understand how vbtable works.

    In the construction time of E, E() constructor just call A::A(0F122Bh) but it didn't use ecx[vbtable] to which the constructor moved dword ptr[eax] or dword ptr[eax+8] or dword ptr [eax+10].

    It just add ecx, dword ptr[this](E::vbtable : 0F7860h), by adding ecx, 1Ch, made ecx point place in A object in E. But, in A, it didn't use this pointer. Also, in E, constructor E() didn't use this point.

    So, I don't know what meaning vbtable has.





    addition :

    By just doing virtual inheritance, the compiler added the vbtable. if then, in my opinion, the compiler should have used the vbtable in the construction time. Nevertheless, if the vbtable was made, the vbtable should have been used.

    But I can't find the example using vbtable in the construction time.

     

    Once, the compiler just use the vbtable in using inherited function.

     

    E:

    func()

    functest();
    00141863 8B 45 F8 mov eax,dword ptr [this] //eax=this
    00141866 8B 08 mov ecx,dword ptr [eax] //ecx=(dword)*this=E::vbtable
    00141868 8B 55 F8 mov edx,dword ptr [this] //edx=this
    0014186B 03 51 04 add edx,dword ptr [ecx+4] //edx=edx+*(dword)[vbtable+4]=this+1c(A offset)
    0014186E 8B CA mov ecx,edx //ecx=edx //I had thought that this information have to be used in next process(in A::functest)
    00141870 E8 1F FA FF FF call A::functest (141294h)

     

    but, nevertheless the compiler gave the [vbtable pointer] ecx, the A::functest never use it. I can't find meaning of vbtable's objection.

    the vbtable have the pointers to each inherited object.

     

    you said to me that,

    "it is point that using virtual base table raise at construct time, but using virtual function table raise before calling function."

     In my opnion, the compiler just add the vbtables to ecx.

    I can't find the use of the ecx[pointer-vbtable]. if ecx is not used, I think it must not been created.

     

    why did the microsoft develpers make the vbtable? I want to find the use of vbtable's pointer, for example ecx.

     

    한국인 같으시니 한국말로 해도 될까요? 영어가 그렇게 유창하지 않아서요..

    김상혁님께서는 E의 생성자 생성 시간에 vbtable을 사용한다고 하셨습니다.

    그러나 제가 보기에는 단지 vbtable들에 대한 포인터를 E의 스택에 넣었을 뿐 입니다. 마치 virtual function처럼

    테이블을 이용해 virtual 상속받은 객체의 생성자들을 호출하진 않죠.

    상속에 오히려 쓰였다고 생각되는 것은

    virtual 상속된 클래스의 객체 생성자 호출시 생성자 호출 직전에 매개변수 외에 넣는 0 또는 1의 값입니다.

     E():A(1,2),Z(3,4),C(1), B(3), D(6), e(8) {}
    00E816C0 55                   push        ebp 
    00E816C1 8B EC                mov         ebp,esp 
    00E816C3 81 EC CC 00 00 00    sub         esp,0CCh 
    00E816C9 53                   push        ebx 
    00E816CA 56                   push        esi 
    00E816CB 57                   push        edi 
    00E816CC 51                   push        ecx 
    00E816CD 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh] 
    00E816D3 B9 33 00 00 00       mov         ecx,33h 
    00E816D8 B8 CC CC CC CC       mov         eax,0CCCCCCCCh 
    00E816DD F3 AB                rep stos    dword ptr es:[edi] 
    00E816DF 59                   pop         ecx 
    00E816E0 89 4D F8             mov         dword ptr [ebp-8],ecx 
    00E816E3 83 7D 08 00          cmp         dword ptr [ebp+8],0 
    00E816E7 74 3B                je          E::E+64h (0E81724h) 
    00E816E9 8B 45 F8             mov         eax,dword ptr [this] 
    00E816EC C7 00 60 78 E8 00    mov         dword ptr [eax],offset E::`vbtable' (0E87860h) 
    00E816F2 8B 45 F8             mov         eax,dword ptr [this] 
    00E816F5 C7 40 08 50 78 E8 00 mov         dword ptr [eax+8],offset E::`vbtable' (0E87850h) 
    00E816FC 8B 45 F8             mov         eax,dword ptr [this] 
    00E816FF C7 40 10 40 78 E8 00 mov         dword ptr [eax+10h],offset E::`vbtable' (0E87840h) 
    00E81706 6A 02                push        2 
    00E81708 6A 01                push        1 
    00E8170A 8B 4D F8             mov         ecx,dword ptr [this] 
    00E8170D 83 C1 1C             add         ecx,1Ch 
    00E81710 E8 0C FB FF FF       call        A::A (0E81221h) 
    00E81715 6A 04                push        4 
    00E81717 6A 03                push        3 
    00E81719 8B 4D F8             mov         ecx,dword ptr [this] 
    00E8171C 83 C1 24             add         ecx,24h 
    00E8171F E8 8A FA FF FF       call        Z::Z (0E811AEh) 
    00E81724 6A 00                push        0 
    00E81726 6A 03                push        3 
    00E81728 8B 4D F8             mov         ecx,dword ptr [this]
    00E8172B E8 1A FA FF FF       call        B::B (0E8114Ah) 

     

     

    B의 생성자 소스

    00E815B3 83 7D 0C 00          cmp         dword ptr [ebp+0Ch],0//   0과 비교해서 같으면 0E815E0으로 jump
    00E815B7 74 27                je          B::B+50h (0E815E0h)          // 따라서 0이나 1을 넣음으로써, A를 상속받을지 안받을지를 결정하죠, 그러니 가상상속 구조시에는 상속을 안받으려고 0을 입력하게 되고, B만 따로 사용할 경우는 1을 넣음으로써 A까지 호출하게 되는구조, 전 이것을 가상상속시 사용되는 메커니즘이라고 보았거든요.

    가상상속이란건, E가 BCD를 상속받음으로써 A와 Z도 3개씩 상속받아야 되서 모호하게 되는것을 방지하기 위해, A와 Z를 하나씩만 상속받는 메커니즘이잖아요.

    그래서 BCD는 아예 상속을 받지 않도록 설정하는게 가상상속의 특징인거죠. 대신 A,Z상속은 E생성할 때 해결하도록. 근데 그 해결법이 vbtable을 사용해야 될 거 같았는데, 전혀 사용을 안한다는게 이상하다는 거죠. vbtable을 스택에 넣어놓기만 하는게 무슨 의미가 있는건가요??

    vbtable의 경우

    E의 생성자()

    00E816EC C7 00 60 78 E8 00 mov dword ptr [eax],offset E::`vbtable' (0E87860h)
    00E816F2 8B 45 F8 mov eax,dword ptr [this]
    00E816F5 C7 40 08 50 78 E8 00 mov dword ptr [eax+8],offset E::`vbtable' (0E87850h)
    00E816FC 8B 45 F8 mov eax,dword ptr [this]
    00E816FF C7 40 10 40 78 E8 00 mov dword ptr [eax+10h],offset E::`vbtable' (0E87840h) 

    여기[E e가 구성된 스택]에 들어가기만 했지, 언제 사용되는지를 모르겠다는 거죠

     

     

    맨 처음에 보신바대로

    E:

    func()

    functest();
    00141863 8B 45 F8 mov eax,dword ptr [this] //eax=this
    00141866 8B 08 mov ecx,dword ptr [eax] //ecx=(dword)*this=E::vbtable
    00141868 8B 55 F8 mov edx,dword ptr [this] //edx=this
    0014186B 03 51 04 add edx,dword ptr [ecx+4] //edx=edx+*(dword)[vbtable+4]=this+1c(A offset)
    0014186E 8B CA mov ecx,edx //ecx=edx //I had thought that this information have to be used in next process(in A::functest)
    00141870 E8 1F FA FF FF call A::functest (141294h)

     여기서 사용되긴 하지만, 보시는 바와 같이 ecx에 A의 위치를 넣는데 사용하기만 했을 뿐, 저 ecx를 A안에서 단지 스택에 넣어두기만 하지, 사용을 하지를 않죠.. 또한 굳이 여기서도 vbtable을 이용해서 구할게 아니라 +14h 와 같이 하드코딩해서 할 수도 있었는데도 불구하고 말이죠.00E81706 6A 02 push 2
    00E81708 6A 01 push 1
    00E8170A 8B 4D F8 mov ecx,dword ptr [this]
    00E8170D 83 C1 1C add ecx,1Ch
    00E81710 E8 0C FB FF FF call A::A (0E81221h)   E생성자의 일부분인데, 여기선 1Ch를 하드코딩해서 더하기 했잖아요? 이렇게 하면 됬을걸 굳이 vbtable을 사용했다는것도 이상하고요.

     

     

    A::

     void functest()
     {
    00E81960 55                   push        ebp 
    00E81961 8B EC                mov         ebp,esp 
    00E81963 81 EC CC 00 00 00    sub         esp,0CCh 
    00E81969 53                   push        ebx 
    00E8196A 56                   push        esi 
    00E8196B 57                   push        edi 
    00E8196C 51                   push        ecx //       단지 여기서 push ecx를 함으로써 스택에 넣어두기만 하고 [백업용]
    00E8196D 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh] 
    00E81973 B9 33 00 00 00       mov         ecx,33h  //ecx에 딴걸 넣음으로써 A의 오프셋은 사라지죠.
    00E81978 B8 CC CC CC CC       mov         eax,0CCCCCCCCh 
    00E8197D F3 AB                rep stos    dword ptr es:[edi] 
    00E8197F 59                   pop         ecx  //물론 여기서 다시 pop ecx 로 받아오긴 하지만요,
    00E81980 89 4D F8             mov         dword ptr [ebp-8],ecx  //그러나 여기서 하는 것도 단지 그 오프셋[A:자기자신]을 스택에 넣는 거밖에 안하잖아요..?
      cout<<"모호한 함수"<<endl;
    00E81983 8B F4                mov         esi,esp 
    00E81985 A1 E0 A2 E8 00       mov         eax,dword ptr [__imp_std::endl (0E8A2E0h)] 
    00E8198A 50                   push        eax 
    00E8198B 68 98 78 E8 00       push        offset string "\xb8\xf0\xc8\xa3\xc7\xd1 \xc7\xd4\xbc\xf6" (0E87898h) 
    00E81990 8B 0D DC A2 E8 00    mov         ecx,dword ptr [__imp_std::cout (0E8A2DCh)] 
    00E81996 51                   push        ecx 
    00E81997 E8 C2 F7 FF FF       call        std::operator<<<std::char_traits<char> > (0E8115Eh) 
    00E8199C 83 C4 08             add         esp,8 
    00E8199F 8B C8                mov         ecx,eax 
    00E819A1 FF 15 D8 A2 E8 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0E8A2D8h)] 
    00E819A7 3B F4                cmp         esi,esp 
    00E819A9 E8 05 F8 FF FF       call        @ILT+430(__RTC_CheckEsp) (0E811B3h) 
     }
    00E819AE 5F                   pop         edi 
    00E819AF 5E                   pop         esi 
    00E819B0 5B                   pop         ebx 
    00E819B1 81 C4 CC 00 00 00    add         esp,0CCh 
    00E819B7 3B EC                cmp         ebp,esp 
    00E819B9 E8 F5 F7 FF FF       call        @ILT+430(__RTC_CheckEsp) (0E811B3h) 
    00E819BE 8B E5                mov         esp,ebp 
    00E819C0 5D                   pop         ebp 
    00E819C1 C3                   ret 

     

    도대체 어디서 저 오프셋을 사용하는 것을 볼 수 있냐는 것이죠.

    이게 제가 궁금한 것입니다.

    사용하질 않을 거라면, 굳이 넣고 빼는 이유가 뭐냐는 거죠.

    저는 그 이유를 호환성으로 보았지만.. 마이크로소프트이 공식입장이 궁금할 뿐입니다.




    • 편집됨 yangil06 2011년 10월 9일 일요일 오후 4:03
    2011년 10월 9일 일요일 오전 5:38
  • 안녕하십니까? yangil06

    Microsoft MSDN Forum 사이트를 방문해 주셔서 감사합니다.

     

    C++ 환경에서는 thiscall calling convention을 기본으로 사용합니다.

    __thiscall http://msdn.microsoft.com/en-us/library/ek8tkfbw(v=vs.80).aspx

     

    Thiscall calling convention에서는 ECX레지스터에 this 포인터를 할당하는 것이 기본 동작 방식입니다. 따라서 member function을 호출하기 위하여 ECX 레지스터를 사용하였고, member function내부에서는 이후 동작을 위하여 사용할 레지스터 값을 preserve하기 위하여 stack에 보관하였습니다.

     

    , 호출규약(calling convention)에 따른 기본 동작 방식으로 이해하시면 될 것입니다. 추가로, 이러한 컴파일러의 동작 방식은 컴파일러의 최적화에 따라 달라질 수 있습니다.

     

    제가 문제에 대해 더 알아야 할 것이 있다면 응답 주시면 감사하겠습니다.

    제시해 드린 답변이 도움이 되었기를 바랍니다.

    2011년 10월 10일 월요일 오전 2:34
    중재자