C + + learning starts from scratch (iii)

Source: Internet
Author: User
Tags constructor inheritance

Similarly, this will also be implicitly type-converted long AB::* p = &AB::B_b;. Note The Ab::b_b type is long B::, an implicit type conversion is made. How do I convert? The original Ab::b_b mapping has an offset of 4, then it will now become 12+4=16 so that ab.*p = 10 can be executed correctly.

At this time to come back to the question just mentioned, AB::ABC can not distinguish, how to do? Note that there are mapping elements a::abc and B::ABC (two ab::abc are caused by both), so you can write AB. A::ABC () to indicate that the call is a function mapped to A::ABC. The type of a::abc here is void (A:: (), and AB is AB and therefore implicitly typed, there is no syntax problem (although A::ABC is not a member of Struct AB, but it is a member of the parent class of AB, C + + allows this, i.e.: ABC's name is also used as part of a matching type. If struct C is also derived from a, there is a c::a, but you cannot write AB. C::a, since the name of the c::a can be known that it does not belong to structure AB. likewise AB. B::ABC (); B::abc will be invoked. Note that the structure A, B, and AB have a member variable named C and the type is long, then AB.C = 10; Whether it will be as before AB. ABC (); No, because there are three ab::c, one of which matches the type of AB and its mapping is 28, so AB.C will return 3028. And if you expect to use the other two ab::c mappings, then write AB as above. A::c and AB. B::c to offset AB's address for implementation.

Note that because of the above, you can do this: void (AB::* pabc) = B::ABC; (AB.*PABC) ();。 The type of b::abc here is void (b::) (), and PABC does not match, but exactly B is the parent class of AB, so an implicit type conversion is made. How do I convert? Because B::ABC maps to addresses, implicit type conversions make sure that the type of this is changed to b* before calling B::abc, so add 12 to change from ab* to b*. Because of the need to add this 12, but B::ABC is not a mapped offset, so pabc actually maps two digits, one is the B::ABC address, one is the offset value of 12, and the result pabc the length of the pointer is no longer 4 bytes as previously said, and becomes 8 bytes ( The extra 4 bytes are used to record the offset value.

Attention should also be paid to the A_b, C, A::C, etc., which are written directly in the AB::ABCD, which should be preceded by a this->, that is a_b = B_b = 2; the actual this->a_b = This->b_b = 2; This is offset two times to get the correct address. Note that the implicit type conversions mentioned above are performed because the permissions at the time of inheritance meet the requirements, or they fail. That is, if the above AB Protection inherits A and private inherits B, only the member functions of AB can be converted as above, and in the member functions of the subclass of AB you will only be able to use members of a and not members of B because permissions are restricted. 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 in c::abcd b_b = 2; and b::c = 24; An error will be given, because this is a subclass of AB, and AB private inherits from B, whose subclasses do not have the right to treat it as B. But there is no implicit type conversion, which can still be achieved by displaying type conversions. and the AB in the main function. A_a = 3; Ab. B_b = 4; Ab. A::ABC () will be an error, because this is an outside call, no permissions, no automatic implicit type conversion.

Note that here C::ABCD and AB::ABCD have the same name, as described above, the member variables of a subclass can have the same name as the member variable of the parent class (above Ab::c and A::c and B::c have the same name), and the member function is even more problematic. Just like the previous one, follow the above mentioned type matching test. Note that because it is a function, you can change the parameter and the function name is the same, which becomes an overloaded function.

Virtual inheritance has already said that when an instance of AB is generated, its length should actually be the length of a plus B, plus the length of the members defined by AB. That is, the instance of AB is also an instance of B, because it is an instance of AB, which records both an instance of a and an instance of B. There is a situation where vegetables and fruits are plants, and marine and breast animals are animals. That is, the inherited two parent classes derive from the same class.

Assume the following:

struct a {long A;};

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

struct D:public B, public C {long D;};

void Main () {D; d.a = 10;}

The example above contains an instance of a, and the instance of C also contains an instance of a. Then the instance of D contains an instance of B and an instance of C, then D contains two instances of a. That is, when D is defined, the mapping elements of the two parent classes are inherited, two mapping elements are generated, the names are d::a, the types are long a::, and the mapped offset values are exactly 0. Results The D.A = 10 in the main function will be an error and cannot confirm which a is used. Isn't that weird? Two mapped elements have the same name, type, and number of mappings! Why do compilers not know to set them as one, because they actually represent different offsets in the instance of D, and 01 are 8. Also, to eliminate the above problem, write D. B::a = 1; D.c::a = 2 to represent member A in a different instance. But the types of b::a and c::a are not all long A::? But as mentioned above, member variables or member functions their own names will also work in type matching, so for D. B::a, because the type on the left is D, it looks to the right, its name is B, exactly the parent of D, the first implicit type conversion, then the type, the A, the implicit type conversion, and then the number. Assuming that the address above D corresponds to 3000, then D. C::a First converts D to an instance of C, so it shifts 3000 to 8 bytes and returns the number 3008 of the type of address of the long type. It is then converted to an instance of a, offset by 0, and finally returned 3008.

This illustrates the problem that member A, who wants to inherit from a, has only one instance, rather than two instances as above. Given that animals have a member variable of starvation, it is clear that whales should simply fill a hunger level, and that two of them will seem odd. In this respect, C + + puts forward the concept of virtual inheritance. The format is that when inheriting the parent class, you can precede the permission syntax with the keyword virtual.

As follows:

struct A {Long A, AA, aaa; void ABC ();}; struct B:virtual public A {long B;};

What is the offset of a virtual inheritance from a,b::b mappings here? will no longer be a length of 12, but 4. The 3 mapped elements of the inheritance are the same as the original, except that the name modification changes to B:: Only, the mappings remain unchanged. So why is B::b 4? The previous 4 bytes were used for what? 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 point to a global array bdiff. What do you mean? The first 4 bytes of an instance of B are used to record an address, which is equivalent to a pointer variable, and the memory recorded by the address identifies the offset value that is caused by the virtual inheritance. The above bdiff[1] means that to convert B instance to instance a, you need to offset the value of bdiff[1] 8, and bdiff[0] represents the offset value 0 that you want to convert B instance to B instance. Why do I have to take a B instance to B? The following description. But why is it an array? Because a class can inherit multiple classes through multiple derivations, the offset value required by each class occupies an element in an array of Bdiff, which is called the Virtual class table.

So 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 above. Suppose B corresponds to an address of 3000. The instance of B is first converted to an instance of a, which should have been offset by 12 and returned 3012, but the compiler found B to be a virtual inheritance from a, then get the offset value 8 by B::p [1], then return 3008, then add 8 to the B::AAA map. Similarly, when b.b = 10, since b::b is not a virtual inheritance, the offset value of 3000 plus b::b mapping is 4 to 3004. and for B. ABC (); the B::p [1] will first be converted to an instance of a and then call A::ABC.

Why do you have to be so troublesome like that? First let's understand what is called virtual (virtual). Emptiness is illusion, it is not true. For example, an old TV set has 10 channels, that is, it can remember the frequency of 10 stations. So it can be said that Channel 1 is the Central 1, 5 channels is the Central 5, 7 channel is Sichuan Taiwan. This is called the channel for us to represent the radio frequency is false, because the channel is not radio frequency, but recorded the frequency of the radio. When we press Channel 5 to switch to Central 5, it is possible that someone has turned over the TV so that 5 channels are no longer central 5, but another station or a snowflake without a signal. Therefore, the virtual is not guaranteed, it may be correct error, because it must be indirectly obtained, in fact, is equivalent to the previous reference. What's the benefit? Just remember to press Channel 5 is Central 5, when you don't want to see central 5 and replaced by Central 2, then the same "by 5 channel" but can get different results, but the program does not have to write, just remember "press 5 channel" and can be realized to the central 2 units to see. So the virtual is indirectly get the result, because of the indirect, the result will be uncertain and appear more flexible, this explains the virtual function can be seen later. But the disadvantage is that more than a program (to be indirectly obtained), less efficient.

Because of the above virtual inheritance, the resulting element is virtual, that is, all operations on the inherited mapped element should indirectly obtain an offset value or address for the corresponding mapping element, but the offset value or address of the inherited mapping element is invariant. The requirement for this scarlet letter is only achieved by changing the value of this by an implicit type conversion. So the offset value required by the B-turn A is obtained indirectly by a pointer B::p to show that it is virtual.

Therefore, the beginning of the said whale will have two degrees of starvation will allow the marine life and the breast animal to inherit from the animal virtual, thus, the member of the starvation of the breast and the marine species will be indirectly used, and then when the whale is derived, the breast and the marine life are directed to the same animal instance ( Because they are indirectly derived from the animal's instance, they indirectly use the members of the animal through virtual inheritance, so that when the whale fills the hunger, it fills the same regardless of which hunger is filled. and C + + is doing just that.

As follows:

struct a {long A;};

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.a = 10;}

When deriving a derived class (that is, determining the offset of each member variable defined in the type definition character "{}" of the derived class) when a virtual inheritance is made from a class, first, the pointers to the virtual class tables mentioned above are arranged to achieve an indirect get offset value, and then the parent classes are sorted, but if there are any parent classes that are virtual inherited in the parent class, the parts are first removed. The derived class then arranges its own mapping elements. Finally, the class that has just been struck out is the virtual inheritance, and if you find that a class that is being inherited has already been sorted, you don't have to repeat that class again, and you no longer have to generate the corresponding mapping element for it.

For the above B, find the virtual inheritance A, first arrange the previous B::p, and then arrange a, but found that a needs to be virtual inheritance, so culling, arranging their own defined mapping element b::b, mapping the offset value of 4 (due to B::p occupancy). The last arrangement of a is to generate the inherited mapping element b::a, so the length of B is 12.

For the above D, found to inherit from C, therefore:

Arrange d::p, accounting for 4 bytes.

Arranges the parent Class B and finds that a is a virtual inheritance, culling, it inherits the mapping element B::b (and B::p), which is automatically generated by the previous compiler, generates D::B, which takes up 4 bytes (the compiler merges B::p and D::p into one, which is known after the virtual function).

Arrange the parent class C, find C need to be virtual inheritance, remove.

Arrange D member D::d with its own definition, the offset value of the mapping is 4+4=8, accounting for 4 bytes.

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

Arranging C, first arranging a in C, finds that it is virtual inheritance, and finds that a has been arranged so that a mapping element is no longer generated for c::a. Then arrange C::p and C::c, which takes up 8 bytes and generates D::C.

So the length of the last structure D is 4+4+4+4+8=24 byte, and there is only one d::a, the type is long a::, the offset value is 0.

If it's faint, it doesn't matter, it just gives an algorithm to implement virtual inheritance, and different compiler vendors give different implementations, so the results that are pushed above may not be correct for some compilers. However, it should be remembered that the meaning of virtual inheritance-all members of a class that is virtual inheritance must be obtained indirectly, and as to how to obtain it indirectly, the different

The compiler has different ways of handling it.

Due to the need to guarantee indirect access, so for long d::* pa = &d::a; because it is long d::*, the compiler discovers that there is a virtual inheritance in the inheritance system of D, and must guarantee the indirect gain of some of its members, so the PA will not be the offset value, otherwise D.*PA = 10; will result in a direct gain of the offset value (the content of the PA is removed), violating the meaning of the virtual inheritance. In order to indirectly access the offset value recorded by the PA, it is necessary to ensure that code execution is indirect when the PA is d::a, and D::d is not indirect. Obviously, this would take more and more complex code, and most compilers would have to do it all with indirect access. The PA will therefore have a length of 8 bytes, one of the 4-byte record offsets, and a 4-byte record for an ordinal number. This ordinal is used for the previously described virtual class table to get the correct offset from the virtual inheritance. Therefore, the value of the first element referred to in the preceding B::p represents an instance of B being converted to B, in order to achieve all indirect access here.

Note that the above D::p are different for instances of different d, except that their content is the same (the address of the virtual class table of structure D). When the instance of D is just generated, the value of the D::p of that instance will be a random number. To ensure that the D::p is properly initialized, the structure d above does not generate a constructor, however, the compiler will automatically generate a default constructor for D (without a constructor for the parameter) to ensure that the D::p and the C::p inherited from C above are properly initialized, resulting in d d = {23, 4}; Because D already has a constructor defined, even if it's not shown on the code.

So what's the point of virtual inheritance? It is a function of indirect acquisition of virtual inheritance, from the type of the normal inheritance without any difference, that is, virtual inheritance and the previous public, such as, is only a grammatical provision, for the type of numbers has no effect. Look at the meaning of the virtual function before you understand 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.