In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
Many novices are not very clear about the call of virtual functions and the internal layout of objects in C++. In order to help you solve this problem, the following editor will explain it in detail. People with this need can come and learn. I hope you can get something.
This time I still use the assembly code generated by analyzing the compiled C++ code to explain the implementation of virtual function calls in C++, and also explain the internal layout of objects in C++ by the way. All of the assembly code below is compiled in VC2005. Although different compilers may produce different results and the internal layout of objects may be different, as long as the compilers meet the C++ standards, the compilation results and the internal layout of objects should be more or less the same.
First, there are two classes with a simple inheritance relationship: class CBase
{
Public:
Virtual void VFun1 () = 0
Virtual void VFun2 () = 0
Void Fun1 ()
}
/ / this is only to generate the assembly code for the function, so the function body is empty.
Void CBase::Fun1 ()
{
}
Class CDerived: public CBase
{
Public:
Virtual void VFun1 ()
Virtual void VFun2 ()
Void Fun2 ()
Private:
Int m_iValue1
Int m_iValue2
}
/ / this is only to generate the assembly code for the function, so the function body is empty.
Void CDerived::VFun1 ()
{
}
/ / this is only to generate the assembly code for the function, so the function body is empty.
Void CDerived::VFun2 ()
{
}
/ / this is to analyze the internal layout of the object, so just assign values to the member variables
Void CDerived::Fun2 ()
{
M_iValue1 = 13
M_iValue2 = 13
}
Now call the member function with the following code:
CDerived derived
/ / call virtual functions with objects
Derived.VFun1 ()
Derived.VFun2 ()
/ / call non-virtual functions with objects
Derived.Fun1 ()
Derived.Fun2 ()
/ / implement polymorphism by calling virtual functions with pointers to the base class of the derived class
CBase * pTest = & derived
PTest- > VFun1 ()
PTest- > VFun2 ()
Here is the assembly code generated after compiling the above code with VC2005:
CDerived derived
0041195E lea ecx, [derived]
00411961 call CDerived::CDerived (411177h)
/ / Code snippet 1
Derived.VFun1 ()
00411966 lea ecx, [derived]
00411969 call CDerived::VFun1 (411078h)
Derived.VFun2 ()
0041196E lea ecx, [derived]
00411971 call CDerived::VFun2 (4111B8h)
Derived.Fun1 ()
00411976 lea ecx, [derived]
00411979 call CBase::Fun1 (411249h)
Derived.Fun2 ()
0041197E lea ecx, [derived]
00411981 call CDerived::Fun2 (4111BDh)
/ / Code snippet 2
CBase * pTest = & derived
00411986 lea eax, [derived]
00411989 mov dword ptr [pTest], eax
PTest- > VFun1 ()
0041198C mov eax,dword ptr [pTest] / / Line 1
0041198F mov edx,dword ptr [eax] / / Line 2
00411991 mov esi,esp
00411993 mov ecx,dword ptr [pTest]
00411996 mov eax,dword ptr [edx] / / Line 3
00411998 call eax / / Line 4
0041199A cmp esi,esp
0041199C call @ ILT+495 (_ _ RTC_CheckEsp) (4111F4h)
PTest- > VFun2 ()
004119A1 mov eax,dword ptr [pTest]
004119A4 mov edx,dword ptr [eax]
004119A6 mov esi,esp
004119A8 mov ecx,dword ptr [pTest]
004119AB mov eax,dword ptr [edx+4] / / Line 5
004119AE call eax
004119B0 cmp esi,esp
004119B2 call @ ILT+495 (_ _ RTC_CheckEsp) (4111F4h)
Through the observation of code snippet 1, we can find that calling the virtual member function of the class through the object is the same as calling the non-virtual member function (the analysis of the assembly code that calls the member function can see my article "Analysis of the this pointer in C++"). In other words, it is impossible to achieve polymorphism with objects.
The following is mainly to analyze the code snippet 2 that implements polymorphism.
Line 1. Put the contents of the first two words of the address pointed to by the pTest pointer (4 bytes, that is, the size of a pointer in a 32-bit system) into the eax register as a pointer
Line 2. Put the value of the pointer in the eax register into the edx register
Line 3. Put the value of the pointer in the dex register into the eax register
Line 4. Call the function pointed to by the eax register
This analysis doesn't seem very clear about how to call the virtual function VFun1 () of the object derived. So let's take a look at the following picture:
This diagram is a hypothetical internal layout of the object derived in memory. The pointer pTest points to the object derived, while the first four bytes of the object derived are a virtual table pointer to the virtual function table.
Looking at this picture and analyzing the assembly code above, it will be much clearer:
Line 1. Get the virtual table pointer value and put it in the eax register
Line 2. Get the value of the virtual table pointer and put it in the edx register
Line 3. Get the value of the address pointed to by the virtual table pointer (that is, VFun1) and put it in the eax register
Line 4. Call the function pointed to by the eax register
Line 5 proves the assumption of the virtual function table in the figure above. The address of the second virtual function VFun2 () is obtained by adding 4 (the size of a pointer in a 32-bit system) to the address of the first virtual function VFun1 ().
Through the above analysis, we can get the calling method of the virtual function in C++: first, get the virtual table pointer in the object; then, find the corresponding virtual table through the virtual table pointer; finally, find the corresponding function to call through the offset in the virtual table.
The following is to prove the existence of the pointer to the virtual function table in the above figure by analyzing the non-virtual member function Fun2 () of class CDerived.
Void CDerived::Fun2 ()
{
004118F0 push ebp
004118F1 mov ebp,esp
004118F3 sub esp,0CCh
004118F9 push ebx
004118FA push esi
004118FB push edi
004118FC push ecx
004118FD lea edi, [ebp-0CCh]
00411903 mov ecx,33h
00411908 mov eax,0CCCCCCCCh
0041190D rep stos dword ptr es: [edi]
0041190F pop ecx
00411910 mov dword ptr [ebp-8], ecx
M_iValue1 = 13
00411913 mov eax,dword ptr [this] / / Line 6
00411916 mov dword ptr [eax+4], 0Dh / / Line 7
M_iValue2 = 13
0041191D mov eax,dword ptr [this]
00411920 mov dword ptr [eax+8], 0Dh
}
00411927 pop edi
00411928 pop esi
00411929 pop ebx
0041192A mov esp,ebp
0041192C pop ebp
0041192D ret
The above is the assembly code for the non-virtual member function Fun2 () of the class CDerived. As you can see, line 6 puts the address pointed to by this in the eax register, while line 7 assigns a value to the address pointed to by the this pointer plus 4 (for a specific analysis, see "the this pointer in C++"), and this address stores the first member variable of class CDerived. We know that the this pointer points to the first address of the object, so why move back 4 bytes when assigning a value to the first member variable? The answer is because the first four bytes of the object are used to store virtual table pointers.
The following code is the C++ code and compiled assembly code for the class without virtual functions in the article "analyzing the this pointer in C++":
Class CTest
{
Public:
Void SetValue ()
Private:
Int m_iValue1
Int m_iValue2
}
Void CTest::SetValue ()
{
M_iValue1 = 13
M_iValue2 = 13
}
Void CTest::SetValue ()
{
004117E0 push ebp
004117E1 mov ebp,esp
004117E3 sub esp,0CCh
004117E9 push ebx
004117EA push esi
004117EB push edi
004117EC push ecx
004117ED lea edi, [ebp-0CCh]
004117F3 mov ecx,33h
004117F8 mov eax,0CCCCCCCCh
004117FD rep stos dword ptr es: [edi]
004117FF pop ecx
00411800 mov dword ptr [ebp-8], ecx
M_iValue1 = 13
00411803 mov eax,dword ptr [this] / / Line 8
00411806 mov dword ptr [eax], 0Dh / / Line 9
M_iValue2 = 13
0041180C mov eax,dword ptr [this]
0041180F mov dword ptr [eax+4], 0Dh
}
00411816 pop edi
00411817 pop esi
00411818 pop ebx
00411819 mov esp,ebp
0041181B pop ebp
0041181C ret
Through the comparison of line 8, line 9 and line 6, line 7, we can see that the first 4 bytes of the object of the class CTest store its first member variable; while the object of the class CDerived stores its first member variable from the fifth byte, and its first 4 bytes are used to store the virtual table pointer. This once again proves the correctness of the internal layout of the objects in the above figure.
Is it helpful for you to read the above content? If you want to know more about the relevant knowledge or read more related articles, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.