C ++ learning starts from scratch (3)

Source: Internet
Author: User
Tags virtual environment

Similarly, long AB: * p = & AB: B _ B; is implicitly converted ;. Note: If the AB: B _ B type is long B:, implicit type conversion is performed. How to convert? If the original AB: B _ B ing offset is 4, it will now be 12 + 4 = 16, so that AB. * p = 10 can be correctly executed ;.

At this time, I came back and thought about the question I just raised. AB: ABC cannot be different. What should I do? Note that there are also ing Elements A: ABC and B: ABC (two AB: ABC are caused by both of them), so you can write AB. a: ABC (); indicates that the function mapped to A: ABC is called. Here the: ABC type is void (A:) (), while AB is AB. Therefore, if implicit type conversion is performed, there is no syntax problem above (although:: ABC is not A member of the structure AB, but it is A member of the parent class of AB. C ++ allows this situation, that is, :: ABC names are also used as part of type matching. For example, if the structure C is also generated by the, there is C: a, but AB cannot be written. c: a, because the name from C: a can be known that it does not belong to the structure AB ). Similarly, AB. B: ABC (); will call B: ABC. Note that the above structure A, B, and AB both have A member variable named c and the type is long, so AB. c = 10; will it return an error like AB. ABC () in front? No, because there are three AB: c, one of which matches the AB type and its ing offset is 28, so AB. c will return 3028. If we want to use the AB ing of the other two AB: c, we can offset the address of AB by writing AB. A: c and AB. B: c.

Note that, as mentioned above, void (AB: * pABC) () = B: ABC; (AB. * pABC )();. The B: ABC type is void (B:) (), which does not match pABC, but B is the parent class of AB. Therefore, implicit type conversion is performed. How to convert? Because B: ABC maps the address, and implicit type conversion must ensure that the type of this is changed to B * Before B: ABC is called *, therefore, we need to add 12 to convert it from AB * to B *. Because we need to add this 12, but B: ABC is not the ing offset value, pABC will actually map two numbers, one is the address corresponding to B: ABC, one is the offset value 12, and the length of the pointer pABC is no longer 4 bytes as previously mentioned, it is changed to 8 bytes (the four extra bytes are used to record the offset value ).

Pay attention to the_ B, c, A: c, and so on directly written in AB: ABCD. They should actually be prefixed with this->, that is, A_ B = B _ B = 2; it is actually this-> A_ B = this-> B _ B = 2; then, as above, this is offset twice to get the correct address. Note that the implicit type conversion mentioned above is performed because the inherited permissions meet the requirements. Otherwise, the operation fails. That is, if the AB protection inherits A and the private inherits B, only the AB member functions can perform the conversion as above, in the member function of the AB subclass, only the member of A can be used, but not the member of B, because the permission is limited. The following will fail.

Struct AB: protected A, private B {...};

Struct C: public AB {void ABCD ();};

Void C: ABCD () {A_ B = 10; B _ B = 2; c = A: c = B: c = 24 ;}

Here, B _ B = 2; and B: C = 24; in c: ABCD, an error is returned because it is a subclass of AB, and AB is private inherited from B, its subclass does not have the permission to regard it as B. But it does not require implicit type conversion. It can still be achieved through display type conversion. The AB. a_a = 3; AB. B _ B = 4; AB. a: ABC (); all errors will be reported, because this is A call initiated outside the world and there is no permission to automatically perform implicit type conversion.

Note that C: ABCD and AB: ABCD have the same name. As mentioned above, the member variables of the subclass can all have the same name with the member variables of the parent class (above AB: c and :: c and B: c. Just perform the type Matching Test as mentioned above. It should be noted that because it is a function, the parameter can be changed, and the function name is still the same, which becomes an overloaded function.

As mentioned above, when an AB instance is generated, its length should actually be the length of A plus the length of B plus the length of Members defined by AB itself. That is to say, the reason why an AB instance is A and B is that an AB instance records both A and B. In this case, both vegetables and fruits are plants, and marine and preserved animals are animals. That is, the inherited two parent classes are derived from the same class.

Assume that:

Struct A {long ;};

Struct B: public A {long B ;}; struct C: public A {long c ;};

Struct D: public B, public C {long d ;};

Void main () {D d; d. a = 10 ;}

The above instance B contains an instance of A, and the instance of C also contains an instance of. Then, the instance D contains a B instance and A C instance, and the instance D contains two A instances. That is, when D is defined, the ing elements of the two parent classes are inherited to generate two ing elements with the names D: a and the types long ::, the ing offset values are exactly 0. Result d. a = 10 in the main function. An error is returned and you cannot determine which a to use. Isn't that strange? The names, types, and mapped numbers of the two ing elements are the same! The compiler does not know why to set them to one, because the offsets actually expressed in the instance D are different. One is 0 and the other is 8. Similarly, to eliminate the above problems, write d. B: a = 1; d. C: a = 2; to indicate a members in different instances. However, the types of B: a and C: a are not all long? However, as mentioned above, the names of member variables or member functions also play a role in type matching, so for d. b: a. Because the type on the left is D, the name on the right is B, which is the parent class of D. implicit type conversion is performed first, and then the type is displayed again, is A, implicit type conversion is performed again, and then A number is returned. Assume that the address corresponding to d is 3000, then d. c: a first converts the instance d to the instance C. Therefore, it offsets the value 3000 by 8 bytes and returns the number 3008 of the long TYPE address type. Then, convert the instance to A. The offset is 0 and 3008 is returned.

The problem described above is that the member A inherited from a has only one instance, rather than two instances as above. Assuming that all animals have a member variable of hunger, it is obvious that a whale only needs to fill in one hunger level, and it is strange to have two hunger levels. C ++ proposes the concept of virtual inheritance. The format is to add the keyword virtual before the permission syntax when inheriting the parent class.

As follows:

Struct A {long a, aa, aaa; void ABC () ;}; struct B: virtual public A {long B ;};

Here, B is inherited from the offset of A, B: B ing? The length of A is no longer 12, but 4. The three ing elements generated by inheritance are the same as the original ones, but the name is changed to B:, and the ing remains unchanged. So why is B: B 4? What are the previous four bytes used? The above is equivalent to the following:

Struct B {long * p; long B; long a, aa, aaa; void ABC ();};

Long BDiff [] = {0, 8}; B: B () {p = BDiff ;}

The above B: p points to a Global Array BDiff. What does it mean? The first four bytes of B's instance are used to record an address, which is equivalent to a pointer variable. The address identified by it records the offset value caused by virtual inheritance in the memory. The above BDiff [1] indicates that to convert instance B to instance A, you need to offset the value of BDiff [1] 8, BDiff [0] indicates that the offset value required for converting B instance to B instance is 0. Why do I need to transfer a B instance to a B instance? It is described later. But why is it an array? Because a class can inherit multiple classes by multiple derivation, the offset value required by each class occupies an element in the BDiff array, it is called a Virtual Class Table ).

Therefore, when writing B B; B. aaa = 20; long a = sizeof (B); the value of a is 20, because a 4 byte is added to record the pointer mentioned above. Assume that the address corresponding to B is 3000. First, the B instance is converted to the instance, which should have been offset by 12 and 3012 is returned. However, if the compiler finds that B is A virtual inherited from A, B :: p [1] gets the expected offset value 8, returns 3008, and then adds B: aaa ing 8 to return 3016. Similarly, when B. B = 10;, because B: B is not inherited by the virtual system, the offset value of 3000 plus B: B ing is 4 to 3004. B. ABC (); converts B to an instance of A through B: p [1] and then calls A: ABC.

Why is it so troublesome as above? First, let's understand what is Virtual ). Virtual is an illusion, not real. For example, an old-fashioned TV has 10 channels, that is, it can remember up to 10 TV stations. Therefore, we can say that channel 1 is the central station, Channel 5 is the central station, and channel 7 is the Sichuan station. Here we call the channel as a false representative of the station frequency, because the channel does not only record the station frequency. When we switch from Channel 5 to five in the center, some people may have already tuned the TV so that channel 5 is no longer the five in the center, but another TV station or simply a snowflake with no signal. Therefore, virtual representation is not guaranteed, and it may be correct or wrong, because it must be obtained indirectly, which is actually equivalent to the reference mentioned earlier. What are the benefits? We only need to remember that the 5 channels are the 5 central servers. If we do not want to watch the 5 central servers and change to the 2 central servers, the same "5 channels" will produce different results, however, the program does not need to be written any more. Simply remember to "Press Channel 5", you can switch to the central two. Therefore, the result is obtained indirectly. The result will be uncertain and more flexible, which can be seen in the subsequent description of the virtual function. However, the disadvantage of virtual reality is that a program (to be indirectly obtained) is added, which is less efficient.

Due to the above virtual inheritance, the inherited elements are all virtual, that is, all operations on the inherited ing elements should indirectly obtain the corresponding offset value or address of the corresponding ing element, however, the offset value or address corresponding to the inherited ing element remains unchanged. Therefore, the red letter must be converted to change the value of this by implicit type conversion. Therefore, the offset value required for converting B to A is obtained indirectly through A pointer B: p to show that B is virtual.

As a result, the whales mentioned at the beginning will have two levels of hunger that will allow both marine creatures and breast animals to inherit from the virtual environment of animals, therefore, it will indirectly use the hunger member of breast animals and marine creatures, and then when the whale class is derived, let breast animals and marine creatures point to the same animal instance (because they both indirectly obtain animal instances and indirectly use animal members through virtual inheritance ), in this way, when a whale is filled with hunger, no matter which hunger is filled with, it is actually filled with the same. C ++ is doing the same thing.

As follows:

Struct A {long ;};

Struct B: virtual public A {long B ;}; struct C: virtual public A {long c ;};

Struct D: public B, virtual public C {long d ;};

Void main () {D d; d. a = 10 ;}

When a class is inherited from a virtual class, it is used to sort the derived class (that is, to determine the offset values of each member variable defined in the Type Definition "{}" of the derived class ), first, sort the pointer of the aforementioned virtual class table to indirectly obtain the offset value, and then sort the parent classes. However, if the parent class contains a parent class that has been inherited by the virtual class, remove these parts first. Sort the ing elements of the derived class. Finally, sort the classes that have just been removed from the virtual hierarchy. If you find that a class that has been inherited by the virtual hierarchy has been arranged, you do not need to sort the classes again, and no longer generates corresponding ing elements for it.

For the above B, if we find that the virtual inheritance A, we first arrange the previously mentioned B: p, and then arrange A, but we find that A needs to be inherited by the virtual, So we remove it, arrange the custom ing Element B: B. The ing offset value is 4 (because B: p occupies ). Finally, A is arranged to generate the inherited ing Element B: a, so the length of B is 12.

For the above D, it is found that it must be inherited from the C virtual, so:

Arranged in four bytes: D: p.

Arrange parent class B and find that "A" is inherited by virtual machines. Therefore, the ing element "B: B" will be inherited (and the previously automatically generated B: p by the compiler ), generate D: B, which occupies 4 bytes (the compiler combines B: p and D: p into one, which will be known later when it comes to virtual functions ).

Arrange the parent class C, and find that C needs to be inherited by virtual, removing.

Arrange the members D: D defined by d. The ing offset value is 4 + 4 = 8, which occupies 4 bytes.

Arrange A and C, first arrange A, which occupies 4 bytes, and generate D:.

Arrange C, first arrange A in C, and find that it is virtual inheritance, and find that it has been arranged by A, and then no longer generates ing elements for C:. C: p and C: c are arranged in 8 bytes to generate D: c.

Therefore, the length of the final structure D is 4 + 4 + 4 + 4 + 8 = 24 bytes, and there is only one D: a, the type is long ::, the offset value is 0.

If the above is dizzy, it doesn't matter. The above just provides an algorithm to Implement Virtual inheritance. Different compiler manufacturers will provide different implementation methods, therefore, the result pushed above may be incorrect for Some compilers. However, remember the meaning of virtual inheritance-all members of the class to be inherited by virtual must be indirectly obtained.

The compiler has different processing methods.

Because indirect acquisition is required, for long D: * pa = & D: a;, because it is long D ::*, the compiler finds that virtual inheritance exists in the inheritance system of D. Therefore, some of its members must be indirectly obtained. Therefore, the values in pa are no longer offset values. Otherwise, d. * pa = 10; the offset value is directly obtained (the content of pa can be extracted), which violates the meaning of virtual inheritance. To indirectly access the offset value recorded by pa, you must ensure that when the code is executed, it is indirect when D: a is put in pa, and D :: d is not indirect. Obviously, this requires more and more complex code. Most compilers use indirect methods to handle this. Therefore, the length of pa is 8 bytes, one of which is a 4-byte record offset, and the other is a 4-byte record with a sequence number. This sequence number is used in the preceding virtual class table to obtain the correct offset caused by virtual inheritance. Therefore, the value of the first element referred to by B: p indicates that instance B is converted to instance B, which is provided to achieve all indirect acquisition.

Note that the above D: p will be different for instances of different D, but their content is the same (all are the addresses of the virtual table with the structure D ). When the instance of D is just generated, the value of D: p of that instance will be a random number. To ensure that D: p is correctly initialized, although the above structure D does not generate a constructor, the compiler will automatically generate a default constructor for D (constructor without parameters) to ensure the correct initialization of D: p and C: p inherited from C above, the result will lead to D d = {23, 4}; error, because D has defined a constructor, even if it is not displayed in the code.

So what is the significance of virtual inheritance? In terms of function, it indirectly obtains instances inherited by virtual resources. In terms of type, it is no different from general inheritance, that is, virtual inheritance is the same as the previous public, etc, it is just a syntax, and has no effect on the number type. Let's take a look at the meaning of the virtual function before learning its meaning.

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.