Object layout is known when the translation of C + + object pointers is address adjusted

Source: Internet
Author: User

I noticed this issue when I was debugging and studying the Netscape browser plugin development. That is, when the object layout is known (that is, there is an inheritance relationship between objects), the compiler adjusts the values of the corresponding pointers based on the object layout when the pointers to the different types of objects are converted (whether implicitly from the bottom up or forced from top to bottom conversions). Either the Microsoft compiler or the GCC compiler will do this because it is related to the C + + object model.

To give a simple example, the following code:

#include <stdio.h>classa{ Public:    intx; voidFoo1 () {printf ("a:foo1 \ n"); };};classD | Publica{ Public:    Doubley; Virtual voidFoo2 () {printf ("b:foo2 \ n"); };};int_tmain (intARGC, _tchar*argv[]) {B* PB = (b*)0x00480010; A* PA =PB; printf ("pb:%p\n pa:%p\n", Pb, PA);    GetChar (); return 0;}

The code above is that B inherits from A,a No virtual function, B has virtual function. Therefore, the start position of the A object does not contain a virtual function table pointer. The start position of the B object contains the virtual function table pointer. In VC 2005, the output is:

pb:00480010
pa:00480018

You can see a difference of 8 bytes between two addresses. The address of two objects is not equal because of the relationship of the virtual function table pointer. Virtual function table pointers typically account for 4 Bytes. The difference in the output is related to the layout of the object, that is, the alignment setting of the object in the compiler's options. In summary, however, there is a compile-time difference between these two addresses. Under different conditions, this difference may also be 4 bytes. For example, if member Y of the B object is changed to int type. This difference is 4 bytes.

In the demo above, the pointer type is implicitly converted from b* to a * and the address value is increased by 8 Bytes. If the pointer type is cast from a * to b*, the address will be adjusted in the opposite direction. Observing the assembly code, you can see that the offset adjustment of this address value is the operation that the compiler inserts at compile time, which is done by the add/sub instruction. Here, the assembly code is no longer displayed.

It is worth mentioning that in C + +, struct and class are essentially no different, just the member's default access level. So in the above code, the use of either class or struct keyword when declaring any object does not affect the conclusion.

The above example briefly illustrates that when an object has an inheritance relationship, the address value may be adjusted during the pointer conversion process, which is done by the compiler. In the above example, the address difference between objects is caused by whether the object header contains virtual function table pointers. Let me give you a more detailed example to illustrate the problem. That is, if an object instance contains multiple child objects (with multiple parent classes) when the address is adjusted. And why, in this case, the destructor for the object must be the virtual function.

The code for the second example is as follows:

  

#include <string.h>#include<stdio.h>//Parent 1classp1{ Public:    intm_x1; intm_x2; intm_x3; Public: P1 () {m_x1=0x12345678; M_X2=0xAABBCCDD; M_x3=0xeeff0011; printf ("P1 constructor.\n"); }    Virtual~P1 () {printf ("P1 destructor.\n"); }    Virtual voidSayhi () {printf ("p1:hello!\n"); }};//Parent 2:16 Bytesclassp2{ Public:    Charm_name[ A]; Public: P2 () {strcpy (M_name,"Jack"); printf ("P2 constructor.\n"); }    Virtual~P2 () {printf ("P2 destructor.\n"); }    Virtual voidShowName () {printf ("P2 Name:%s\n", M_name); }};//Parent 3:16 Bytesclassp3{ Public:    Charm_nick[ A]; Public: P3 () {strcpy (M_nick,"Fafa"); printf ("P3 constructor.\n"); }    Virtual~P3 () {printf ("P3 destructor.\n"); }    Virtual voidShownick () {printf ("P3 Nick:%s\n", M_nick); }};//Child1classC1: PublicP1, PublicP2, Publicp3{ Public:    intm_y1; intM_y2; intM_y3; intM_y4; Public: C1 () {m_y1=0x01; M_y2=0x02; M_y3=0x03; M_y4=0x04; printf ("C1 constructor.\n"); }    Virtual~C1 () {printf ("C1 destructor.\n"); }    Virtual voidSayhi () {printf ("c1:sayhi\n"); }    Virtual voidc1_func_01 () {printf ("c1:c1_func_01\n"); }};int_tmain (intARGC, _tchar*argv[]) {C1*C1 =NewC1 (); P1*P1 =C1; P2*P2 =C1; P3*P3 =C1; P1-Sayhi (); printf ("C1:%p\np1:%p\np2:%p\np3:%p\n", C1, p1, p2, p3); printf ("&c1->m_y4:%p\n", &c1->m_y4); //Show object ' s binary dataUnsignedChar* Pbytes = (unsignedChar*) (C1); //_crtmemblockheader *phead = PhDr (pbytes);size_t cb =sizeof(C1); unsignedinti;  for(i =0; I < CB; i++) {printf ("%02x", Pbytes[i] &0xFF); if((I &0xF) ==0xF) printf ("\ n"); } printf ("\ n"); //_CrtDumpMemoryLeaks ();    DeleteP2; return 0;}

The second example of the main content is: Subclass C1, with three parent class: P1,p2,p3. All classes have virtual destructors, that is, object instances have virtual function table pointers. The inheritance relationship of the class is shown:

  

  

Figure 1. Inheritance relationships for classes

When a class C1 is constructed, it will contain three sub-objects: P1,P2,P3. We know that the first parent class P1 the virtual function table pointer, is the use of the virtual function table pointer C1, that is, the subclass has the ability to cover the virtual function of the parent class, which is the important part of implementing polymorphism in C + +. Therefore, in the C1 object instance, there is virtually no P1 virtual function table pointer. Instead, the subclass is used directly. So P2 and P3 are also C1 's parents, how is the contents of the virtual function table of P2 and P3 obtained? This involves the C + + object model.

P2,P3 's virtual function table cannot be merged with the contents of C1 's virtual function table, which makes it difficult for the compiler to implement calls to P2,P3 's virtual functions. Instead, they are offset backwards, except for the first parent class, in which other parent classes keep a separate virtual function table pointer in their object. That is, the object has an independent perspective of P2,P3. In this example, the object has a total of three virtual function table pointers, three angles of view: P1/c1,p2,p3. The object model looks like this:

  

Figure 2. Object model with multiple sub-objects

Gives an object model of an instance of C1. When a pointer to C1 is converted to a pointer to P2 or P3, it has been said earlier that the compiler has inserted an adjustment to the address value. In this example, I set the size of the member variable to occupy space, so that the address offset value is 0x10,0x20. The above code produces the following output:

P1 constructor.
P2 constructor.
P3 constructor.
C1 constructor.
C1:sayhi
c1:003e5068
p1:003e5068
p2:003e5078
p3:003e5088
&c1->m_y4:003e50a4


B8, DD, CC BB AA, FF EE
A8 4 a 6B CD CD CD CD CD CD
98 (CD CD CD CD CD)
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00

C1 destructor.
P3 destructor.
P2 destructor.
P1 destructor.

In the middle part of the output, the binary content of the object is given, and the dump is forthcoming. You can see the first behavior P1/c1 perspective. The second behavior P2 angle of view, third behavior P3 angle of view. The member variable of the IV behavior C1.

You can also see that when you call delete on a pointer to p2*, the object can be properly refactored. This is because when the compiler constructs the C1 object, because the P2,p3 destructor is a virtual function, the compiler also adds address adjustment processing to its destructor. Because the compiler knows the layout of the p2,p3 relative to the C1, it is aware of the object's true memory starting point, so it inserts the corresponding trunk code in the code snippet, subtracts the object address from the offset value, gets the object's actual address, and jumps to C1 's destructor. These results are obtained by disassembling the output of the debug version. Here, the display and analysis of the assembly code is omitted.

Assuming that the virtual keyword is removed from the P2 destructor, the code that runs the above will eject the error. So at this point the compiler simply P2 the value of the pointer as an actual P2 object address to be refactored, that is, it will try to free this address value. And it's obvious that it's wrong. In debug mode, the following assertion Fail dialog box is displayed:

  

So, from the example above, we can see why the fictional function of a class is defined as a virtual function. In the effective C + + book, this is said, if the imaginary function is not virtual, then this object may only be semi-destructor. Of course, for an ordinary single-inherited object, if the instance has only one virtual function table pointer, there is no need for additional processing if the subclass is a basic data type, which in fact does not cause any problems. Because the memory is allocated, the block of information in front of the memory already describes the size of the memory. So there is no error in releasing the memory link. However, a problem occurs if the child class object is also required to be disposed of in the member. For example, when a member points to memory for a dynamic request, it is clear that they will become a memory leak state.

  Conclusion:

Through the above analysis, we can see that

(1) A pointer-type conversion between types that have an inheritance relationship, and the compiler adds an address adjustment at the time of conversion.

(2) When there are more than one parent class and the parent class imaginary function is a virtual function, because the child object is offset from the object base address, the compiler also inserts a trunk code for each parent class that has an offset (not the first in the parent class list), and first adjusts the address to the actual object address. It then jumps to the destructor of the actual object to ensure that the object is properly refactored.

Object layout is known when the translation of C + + object pointers is address adjusted

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.