Exploring the implementation of the c ++ Mechanism

Source: Internet
Author: User

I used to learn C ++ by myself. Now, in retrospect, I didn't understand anything at the time. C ++ cannot be used, but C ++ is leading the way. In high school, noip is not allowed to use the STL library. in the competition, the C ++ object-oriented mechanism is basically useless. Therefore, the noip name in high school is C ++, in fact, cout and CIN are added to C.

A few days ago, I read the old CAPTCHA document by Han, which recorded some C ++ object-oriented mechanism explorations and aroused my interest. This semester I learned the compilation by myself and provided me with the capability foundation for my own practical exploration. After I got started, I looked at the implementation of the C ++ mechanism from a more underlying perspective, in the dark, I was able to touch the reins of taming C ++.

Reference:

Essentially, it is a pointer, which should be guessed even if you have not read the disassembly.

 

Object layout in memory:
 
1: ClassFather

 
2:{

 
3:IntIA _;

4:IntIB _;

 
5:

 
6:VoidFunca ();

 
7:VoidFuncb ();

 
8:};

 
9:

 
10: ClassChild: Father

 
11:{

 
12:IntIC _;

 
13:VoidFuncc ();

 
14:};

A father object only contains (low address> high address): IA _, IB _. That is, the size of a father object is 8 bytes, and the function does not occupy the memory space.
Why not? In fact, the member functions of the class can be seen as essentially the same as normal functions. The compiler knows the position of the function during compilation, so the address (offset) of the call function is directly called when a common function is called ). That is, if the function is hard-coded, the address of the function is fixed (regardless of the location ). The call of member functions is also the case, but the compiler has done more to determine whether the object has the "permission" to call the function (the function is not declared by you, of course, you do not have the permission to call this method. If you do not have the permission, an error is returned, indicating that the object type does not have this method. Therefore, the size of the class object has nothing to do with the number of methods of the class. Member functions are essentially the same as common functions. to implement this mechanism, the compiler is required to do the work.This pointer:One of the differences between a member function and a common function is to access the data of an object. To access the element of an object, we need to find the memory location where the element is located, that is, to have a pointer. We didn't see passing the this pointer, because it was done by the compiler for us. The disassembly will show that when the object calls a method, it will assign the first address of this object to the ECX register and pass this pointer through the Register. In member functions, we can call object elements without explicitly writing this pointer, or because the compiler has helped us perform a step of "translation ".Privatization:Not to mention, the compiler judges whether an element can be accessed through the source code in the compilation phase, whether a method can be called, and there is no access restriction during running. ViewCode:

 
1: # Include<Stdio. h>

2:

 
3: ClassExp

 
4:{

 
5:IntIA _;

 
6:IntIB _;

 
7:

 
8: Public:

9:Exp ()

 
10:{

 
11:IA _ = IB _ = 0;

 
12:}

 
13:VoidOut ()

 
14:{

 
15:Printf ("% D \ t % d \ n", Ia _, IB _);

16:}

 
17:};

 
18:

 
19: IntMain ()

 
20:{

 
21:Exp OA;

 
22:Void* Pc = & OA;

23:

 
24:Oa. Out ();

 
25:*(Int*) Pc = 1;

 
26:*(Int*)((Int) PC + 4) = 2;

 
27:Oa. Out ();

 
28:

29:Return0;

 
30:}

Result: 0 0

1 2

Although Ia _ and IB _ are private, they are modified by the outside world. Because the compiler cannot know what I did (the explicit Oa. Ia _ = 1 was discovered)

 

Structure and structure:

The compiler helped us do more work and generated some additional code.

Note that:

 
1: VoidTest (father OP)

 
2:{

3:}

 
4:

 
5: IntMain ()

 
6:{

 
7:Father OA;

 
8:Test (OA );

 
9:Return0;

10:}

 

Will call the copy constructor.

 

Overload:

The same is true for the compiler. The final function name generated by C ++ is related to the parameter. Therefore, the name of the function generated by different parameters is different. It seems that the name of the function is the same, but it is actually different. When a function is called, the compiler determines the parameter type and generates a function name for "matching ". (Of course, this is not only simple, but also the case of type conversion will be considered)

 

Inheritance:

From the perspective of memory Layout

 
1: StructChild: Father

And

 
1: StructChild

 
2:{

3:Father O;

 
4:// Other

 
5:};

 

Same (virtual functions will be discussed later ). The first part of the subclass is the same as the parent class.

Therefore, a function that accepts the father * parameter can accept the child * parameter, and the conversion is safe.

Functions with Father & type parameters can accept Child &, but the inheritance method must be public. But, why?

In the protected and private inheritance modes, the interfaces of the parent class inherited by sub-classes are hidden externally. Therefore, in principle, all the method elements of a father & passed-in parameter are unavailable, if the rule is used, the compiler determines this and reports an error.

 

Virtual functions:

This is especially true.

Question: Why do I need virtual functions?

Answer: The basic class can manage the sub-class's acquaintance function through virtual functions. (My c ++ primer is missing after it is borrowed, so it can only be found online ).

I will not elaborate on the specific details of the virtual function. I will discuss the underlying mechanism.

To implement virtual functions, each class with virtual functions has a corresponding virtual table. This virtual table is stored in the read-only memory area and records the address of the corresponding function. (PS: A class has only one virtual table)

Each class object needs to save a virtual table pointer and the virtual table address of this class. Therefore, you use the father * pointer to point to a child object, and the virtual function called is child.

The virtual table pointer is saved in the header of each object.

 
1: ClassChild: Father

 
2:{

 
3:IntIC _;

 
4:VoidFuncc ();

5:Virtual VoidVF ();

 
6:};

The child object now has four more bytes than the previous one. Memory layout (from low address to high address): virtual table pointer _ vfptr, Ia _, IB _, IC _.

Okay. The problem is that child inherits father, but the father function is not customized for child. That is to say, whether it is a father object or a child object, they call funca () to use the same function. However, Father does not have _ vfptr. The child object has this added to the header. Isn't it correct to use this pointer in funca () to locate Ia _ and IB?

The phenomenon tells us that funca () can correctly access Ia _ and IB _. Therefore, it is assumed that when the child object calls funca, it is not the real header address, it is offset by four bytes.

This is true for disassembly. In this case, the father class cannot call virtual functions? Of course, Father does not know how to call the virtual function in funca.

There is also an interesting phenomenon:

 
1: # Include<Stdio. h>

2:

 
3: ClassBase

 
4:{

 
5: Public:

 
6:Virtual VoidShowid ()

 
7:{

8:Printf ("Base \ n");

 
9:}

 
10:};

 
11:

 
12: ClassCB:PublicBase

 
13:{

 
14: Public:

 
15:Virtual VoidShowid ()

 
16:{

 
17:Printf ("CB \ n");

 
18:}

 
19:};

 
20:

21: ClassCc:PublicBase

 
22:{

 
23: Public:

 
24:Virtual VoidShowid ()

 
25:{

 
26:Printf ("Cc \ n");

27:}

 
28:};

 
29:

 
30: VoidTest (CB & ob)

 
31:{

 
32:Ob. showid ();

 
33:}

 
34:

 
35: IntMain ()

 
36:{

 
37:Base obase;

 
38:CB ob;

 
39:Cc oc;

 
40:

 
41:CB * PCB = & ob;

42:

 
43:*(Int*) (& Ob) = *(Int*) (& OC );// Modify the virtual table pointer

 
44:Ob. showid ();

 
45:(CB *) (& ob)-> showid ();

 
46:PCB-> showid ();

 
47:Test (OB );

48:

 
49:Return0;

 
50:}

Guess the result. Buy it and leave it.

Result: CB CC

Modify the virtual table pointer of ob in 43 rows to point it to the virtual table of the CC class.

However, ob. showid () ignores our changes and calls the showid of the CB class. In the disassembly, he found that he did not go through the "getting the virtual table pointer and getting the corresponding function address in the virtual table" and called it directly. Because the average person will not be idle to change the virtual table pointer of the object, the object type is clear, the compiler can determine the called function address through this information, so there is no need to take his set, this is more efficient.

The PCB-> showid () is different. He walked the process very well, because a parent class pointer can point to a subclass object, and the compiler cannot find information, so he went through the process.

Now we are struggling to Output CB for Shenma (CB *) (& ob)-> showid.

In the disassembly, we found that the compiler was independent and did not go through the pointer process.

Then you can guess (base *) (& ob)-> showid (); what is output? Cc.

By comparing the differences between the two, we can find some clues, such as when to go through the process and when not to go.

Finally, it is test (OB). As mentioned above, the reference is essentially a pointer, so this result is very understandable.

Also, I thought about

 
1: VoidTest2 (Base op)

 
2:{

 
3:Op. showid ();

 
4:}

Are there any virtual table pointers copied? Try it and you will know, er... No.

As mentioned above, the copy constructor will be called, but you have not written a value assignment for the virtual table pointer in this function. But the evil compiler has quietly added it to you ~. (AlAs? What about the exercises)

 

 

Rtti

Each class has a specific virtual table address, and each object will save the virtual table address. You should have thought of it. It's too lazy to write it.

 

In summary. As you can see, the object-oriented mechanism is not very special at the underlying layer, and the implementation of the mechanism depends mainly on the compiler.

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.