Thread: Virtual function and multiple inheritance

  1. #31
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by George2 View Post
    Which exact statements in the assembly language do you mean? Could you quote please?
    "It actually takes the first 4 bytes and dereferences it to call the appropriate function"
    0041502E mov eax,dword ptr [ebp-140h]
    00415034 mov edx,dword ptr [eax]
    00415036 mov esi,esp
    00415038 mov ecx,dword ptr [ebp-140h]
    0041503E mov eax,dword ptr [edx]
    00415040 call eax

    Let's take a look at this assembly. At the beginning of the assembly, the pointer itself is stored within eax. The pointer to the class, the object.
    So the next dereferences that pointer and stores it in edx. So it dereferences and stores a 4-byte value in edx. What's the first 4 bytes in a class? The vtable!
    The next line is irrelevant.
    But the next tells me that the compiler stores the class pointer in ecx, which is basically the "this" register. The register where the "this" pointer is stored.
    Moving on, we see that the code dereferences edx, which previously held the value of the dereferenced class pointer, the vtable pointer. And then it calls this address, entering the virtual function.
    So that tells me the class looks something like:
    Code:
    class D
    {
    	VTablePtr* ptr;
    	// ...
    };
    And I have a pointer to the class object, pD. So the code is essentially, (*pD->ptr)();

    And it is appreciated if you could post you code, and it is good if we could discuss based on the common code base. :-)
    I already did. Look up and see the code I used both in C++ and assembly plus the class definition.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  2. #32
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I am not sure what do you mean. Does your above reply relates to my question?
    No, to iMalc's comment. When I don't quote a question, you can assume that it relates to the post directly above. Actually, you can assume that for most posters, because that' standard protocol. As is top-quoting only the relevant parts of the thing you reply to.

    (And when someone posts while I'm typing, I have to edit the post to insert the quote.)
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #33
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Hi Elysia,


    Why I do not have similar assembly code generated? Here is my source codes and generated assembly code, looks like there is no such indirection through vtable?

    Code:
    class Foo {
    public:
    	virtual int foo() {return 1;}
    	int goo() {return 2;}
    };
    
    int main()
    {
    	Foo f;
    	f.foo();
    	f.goo();
    
    	return 0;
    }
    Code:
    int main()
    {
    00941410  push        ebp  
    00941411  mov         ebp,esp 
    00941413  sub         esp,0CCh 
    00941419  push        ebx  
    0094141A  push        esi  
    0094141B  push        edi  
    0094141C  lea         edi,[ebp-0CCh] 
    00941422  mov         ecx,33h 
    00941427  mov         eax,0CCCCCCCCh 
    0094142C  rep stos    dword ptr es:[edi] 
    	Foo f;
    0094142E  lea         ecx,[f] 
    00941431  call        Foo::Foo (9410DCh) 
    	f.foo();
    00941436  lea         ecx,[f] 
    00941439  call        Foo::foo (941055h) 
    	f.goo();
    0094143E  lea         ecx,[f] 
    00941441  call        Foo::goo (941109h)

    regards,
    George

    Quote Originally Posted by Elysia View Post
    0041502E mov eax,dword ptr [ebp-140h]
    00415034 mov edx,dword ptr [eax]
    00415036 mov esi,esp
    00415038 mov ecx,dword ptr [ebp-140h]
    0041503E mov eax,dword ptr [edx]
    00415040 call eax

    Let's take a look at this assembly. At the beginning of the assembly, the pointer itself is stored within eax. The pointer to the class, the object.
    So the next dereferences that pointer and stores it in edx. So it dereferences and stores a 4-byte value in edx. What's the first 4 bytes in a class? The vtable!
    The next line is irrelevant.
    But the next tells me that the compiler stores the class pointer in ecx, which is basically the "this" register. The register where the "this" pointer is stored.
    Moving on, we see that the code dereferences edx, which previously held the value of the dereferenced class pointer, the vtable pointer. And then it calls this address, entering the virtual function.
    So that tells me the class looks something like:
    Code:
    class D
    {
    	VTablePtr* ptr;
    	// ...
    };
    And I have a pointer to the class object, pD. So the code is essentially, (*pD->ptr)();


    I already did. Look up and see the code I used both in C++ and assembly plus the class definition.

  4. #34
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    You must call the function through a pointer. That's the whole ordeal. If you have a local object of type D, then it cannot be type C or Bo or anything else so the compiler knows it can call foo directly.
    However, if you have a pointer, then the pointer can be of the base class's type, yet point to a derived type, thus paving way for polymorphism and the vtable.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #35
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Elysia,


    I have tested your reply and you are correct. I have got similar result as yours. I have studied your previous statement about the generated assembly again. Looks great!

    Just two more comments,

    1.

    The address of vtable and the address of this pointer are the same, right?

    2.

    How do you know ptr [ebp-140h] contains the address to current pointer?

    --------------------
    0041502E mov eax,dword ptr [ebp-140h]
    00415034 mov edx,dword ptr [eax]
    00415036 mov esi,esp
    00415038 mov ecx,dword ptr [ebp-140h]
    0041503E mov eax,dword ptr [edx]
    00415040 call eax

    Let's take a look at this assembly. At the beginning of the assembly, the pointer itself is stored within eax. The pointer to the class, the object.
    So the next dereferences that pointer and stores it in edx. So it dereferences and stores a 4-byte value in edx. What's the first 4 bytes in a class? The vtable!
    The next line is irrelevant.
    But the next tells me that the compiler stores the class pointer in ecx, which is basically the "this" register. The register where the "this" pointer is stored.
    Moving on, we see that the code dereferences edx, which previously held the value of the dereferenced class pointer, the vtable pointer. And then it calls this address, entering the virtual function.
    --------------------

    Quote Originally Posted by Elysia View Post
    You must call the function through a pointer. That's the whole ordeal. If you have a local object of type D, then it cannot be type C or Bo or anything else so the compiler knows it can call foo directly.
    However, if you have a pointer, then the pointer can be of the base class's type, yet point to a derived type, thus paving way for polymorphism and the vtable.

    regards,
    George

  6. #36
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    1. Yes, the vtable is the first item inside the object [although there are other ways to store the vtable, this form makes it easy to follow and it stays constant for different derived classes [if you put the vtable at the end of the structure, it would move based on the number of elements within the struct, for example].

    2. From the code-snippet quoted, we can't KNOW what it does. But we can GUESS that EBP-140 is the address of the object. The "this" pointer is commonly stored in a register to avoid stacking overhead [storing the pointer on the stack will be one store, and then a load almost immediately when the call is made - as accessing the "this" pointer in the function called is usually one of the first things actually done in the called function]. MS appears to choose ECX.

    The only way to KNOW what's going on is to understand the entire function from start to finish.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #37
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Mats,


    I have made some further experiement about the generated assembly code for non-virtual function.

    I am confused why there is instruction like,

    00BC14EA mov ecx,dword ptr [pf]
    00BC14F2 mov ecx,dword ptr [pf]
    00BC14FA mov ecx,dword ptr [pb]

    I think we can get the address of non-virtual address function directly, and I can not find the usage of register ecx in the code below. Any ideas?

    Code:
    int main()
    {
    00BC14C0  push        ebp  
    00BC14C1  mov         ebp,esp 
    00BC14C3  sub         esp,0E4h 
    00BC14C9  push        ebx  
    00BC14CA  push        esi  
    00BC14CB  push        edi  
    00BC14CC  lea         edi,[ebp-0E4h] 
    00BC14D2  mov         ecx,39h 
    00BC14D7  mov         eax,0CCCCCCCCh 
    00BC14DC  rep stos    dword ptr es:[edi] 
    	Foo f;
    	Base* pb = &f;
    00BC14DE  lea         eax,[f] 
    00BC14E1  mov         dword ptr [pb],eax 
    	Foo* pf = &f;
    00BC14E4  lea         eax,[f] 
    00BC14E7  mov         dword ptr [pf],eax 
    	pf->foo();
    00BC14EA  mov         ecx,dword ptr [pf] 
    00BC14ED  call        Foo::foo (0BC1064h) 
    	pf->goo();
    00BC14F2  mov         ecx,dword ptr [pf] 
    00BC14F5  call        Foo::goo (0BC100Ah) 
    	pb->foo();
    00BC14FA  mov         ecx,dword ptr [pb] 
    00BC14FD  call        Base::foo (0BC103Ch) 
    	return 0;
    00BC1502  xor         eax,eax
    Code:
    #include <iostream>
    
    using namespace std;
    
    class Base {
    public:
    	void foo() {cout << "-1" << endl;}
    };
    
    class Foo: public Base {
    public:
    	void foo() {cout << "1" << endl;}
    	void goo() {cout << "2" << endl;}
    };
    
    int main()
    {
    	Foo f;
    	Base* pb = &f;
    	Foo* pf = &f;
    	pf->foo();
    	pf->goo();
    	pb->foo();
    	return 0;
    }

    Quote Originally Posted by matsp View Post
    1. Yes, the vtable is the first item inside the object [although there are other ways to store the vtable, this form makes it easy to follow and it stays constant for different derived classes [if you put the vtable at the end of the structure, it would move based on the number of elements within the struct, for example].

    2. From the code-snippet quoted, we can't KNOW what it does. But we can GUESS that EBP-140 is the address of the object. The "this" pointer is commonly stored in a register to avoid stacking overhead [storing the pointer on the stack will be one store, and then a load almost immediately when the call is made - as accessing the "this" pointer in the function called is usually one of the first things actually done in the called function]. MS appears to choose ECX.

    The only way to KNOW what's going on is to understand the entire function from start to finish.

    --
    Mats

    regards,
    George

  8. #38
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Some clarifying comments in red below.
    Code:
    int main()
    {
    00BC14C0  push        ebp  
    00BC14C1  mov         ebp,esp 
    00BC14C3  sub         esp,0E4h 
    00BC14C9  push        ebx  
    00BC14CA  push        esi  
    00BC14CB  push        edi  
    00BC14CC  lea         edi,[ebp-0E4h] 
    00BC14D2  mov         ecx,39h 
    00BC14D7  mov         eax,0CCCCCCCCh 
    00BC14DC  rep stos    dword ptr es:[edi] 
    	Foo f;
    	Base* pb = &f;
    00BC14DE  lea         eax,[f] 
    00BC14E1  mov         dword ptr [pb],eax    pb = &f
    	Foo* pf = &f;
    00BC14E4  lea         eax,[f] 
    00BC14E7  mov         dword ptr [pf],eax     pf = &f
    	pf->foo();
    00BC14EA  mov         ecx,dword ptr [pf]      this = pf
    00BC14ED  call        Foo::foo (0BC1064h) 
    	pf->goo();
    00BC14F2  mov         ecx,dword ptr [pf]    this = pf
    00BC14F5  call        Foo::goo (0BC100Ah) 
    	pb->foo();
    00BC14FA  mov         ecx,dword ptr [pb]  this = pb
    00BC14FD  call        Base::foo (0BC103Ch) 
    	return 0;
    00BC1502  xor         eax,eax
    Clearly the code is non-optimized debug build, as it's filling the stack with 0xCC, and it's doing two lea eax, [f] with one instruction that doesn't destroy eax in between.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #39
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Mats,


    So ecx is used to contain this pointer?

    Quote Originally Posted by matsp View Post
    Some clarifying comments in red below.
    Code:
    int main()
    {
    00BC14C0  push        ebp  
    00BC14C1  mov         ebp,esp 
    00BC14C3  sub         esp,0E4h 
    00BC14C9  push        ebx  
    00BC14CA  push        esi  
    00BC14CB  push        edi  
    00BC14CC  lea         edi,[ebp-0E4h] 
    00BC14D2  mov         ecx,39h 
    00BC14D7  mov         eax,0CCCCCCCCh 
    00BC14DC  rep stos    dword ptr es:[edi] 
    	Foo f;
    	Base* pb = &f;
    00BC14DE  lea         eax,[f] 
    00BC14E1  mov         dword ptr [pb],eax    pb = &f
    	Foo* pf = &f;
    00BC14E4  lea         eax,[f] 
    00BC14E7  mov         dword ptr [pf],eax     pf = &f
    	pf->foo();
    00BC14EA  mov         ecx,dword ptr [pf]      this = pf
    00BC14ED  call        Foo::foo (0BC1064h) 
    	pf->goo();
    00BC14F2  mov         ecx,dword ptr [pf]    this = pf
    00BC14F5  call        Foo::goo (0BC100Ah) 
    	pb->foo();
    00BC14FA  mov         ecx,dword ptr [pb]  this = pb
    00BC14FD  call        Base::foo (0BC103Ch) 
    	return 0;
    00BC1502  xor         eax,eax
    Clearly the code is non-optimized debug build, as it's filling the stack with 0xCC, and it's doing two lea eax, [f] with one instruction that doesn't destroy eax in between.

    --
    Mats

    regards,
    George

  10. #40
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    In MS's calling convention, yes.

    And don't quote huge posts to answer with a one-liner.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #41
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks CornedBee,


    Good to know MS always use ecx to hold this pointer.

    > And don't quote huge posts to answer with a one-liner.

    I will take into consideration in the future. :-)

    Quote Originally Posted by CornedBee View Post
    In MS's calling convention, yes.

    And don't quote huge posts to answer with a one-liner.

    regards,
    George

  12. #42
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by George2 View Post
    Good to know MS always use ecx to hold this pointer.
    No, this is not guaranteed. I have seen assembly code when the this pointer is pushed onto the stack. Don't rely on assembly for these things.

    And when I see mov eax, [ebp-140h] I see that it is the first instruction of the line of code and I since I trust the compiler, I know that's the pointer it's putting into eax. If you're skeptical, you could always execute that first assembly instruction and compare the value of eax to the address of the pointer. They should match.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #43
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    No, this is not guaranteed. I have seen assembly code when the this pointer is pushed onto the stack. Don't rely on assembly for these things.
    Only when the function is varargs, at least in the .Net version (7.0) of Visual C++.

    Look up "thiscall" in the help section in your VS.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #44
    Registered User
    Join Date
    May 2006
    Posts
    1,579
    Thanks Elysia and Mats,


    Do you know whether Microsoft provides online free manual for the assembly language guide with Visual Studio (i.e. how Visual Studio compiler's assembly language looks like, e.g. which register is used to hold this pointer or something, other than a general assembly language guide)?

    Quote Originally Posted by matsp View Post
    Only when the function is varargs, at least in the .Net version (7.0) of Visual C++.

    Look up "thiscall" in the help section in your VS.

    --
    Mats

    regards,
    George

  15. #45
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    The probably have an ABI guide somewhere, but they'd be pretty stupid to document anything beyond that.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. What is a virtual function pointer?
    By ting in forum C++ Programming
    Replies: 4
    Last Post: 03-05-2008, 02:36 AM
  2. Multiple Inheritance - Size of Classes?
    By Zeusbwr in forum C++ Programming
    Replies: 10
    Last Post: 11-26-2004, 09:04 AM
  3. inheritance and performance
    By kuhnmi in forum C++ Programming
    Replies: 5
    Last Post: 08-04-2004, 12:46 PM
  4. structure vs class
    By sana in forum C++ Programming
    Replies: 13
    Last Post: 12-02-2002, 07:18 AM
  5. Exporting Object Hierarchies from a DLL
    By andy668 in forum C++ Programming
    Replies: 0
    Last Post: 10-20-2001, 01:26 PM