C + + learning starts from scratch (iv)

Source: Internet
Author: User
Tags constructor diff inheritance

virtual function

Virtual inheritance of a function type of mapping elements, according to virtual inheritance, it should be indirectly obtained the address of this function, but the result is to indirectly obtain the value of this parameter. In order to obtain the address of the function indirectly, C + + proposes a kind of syntax--virtual function. When writing a function declaration or definition in the type definition character "{}", add the keyword virtual before declaring or defining the statement, as follows:

struct a {long A; virtual void ABC (), BCD ();

void A::abc () {a = $} void A::bcd () {a = 5;}

The above is equivalent to the following:

struct a {void (a::* PF) (); long A; void ABC (), BCD (); A (); };

void A::abc () {a = $} void A::bcd () {a = 5;}

void (A::* avf[]) () = {a::abc, A::BCD}; void A::a () {PF = AVF;}

Here a member A::p F, like the previous Virtual class table, is a pointer to an array that is called the virtual function table (the virtual functions tables) and is an array of function pointers. When the A::ABC is used, it is obtained indirectly by a::p f by giving the ordinal number of the A::ABC in a::p F; A.ABC (); will be equated (a.* (a.pf[0)) ();. So the length of structure A is 8 bytes, and then look at the following code:

struct B:public A {long B; void ABC ();}; struct C:public A {long C; virtual void

ABC (); };

struct Bb:public B {long BB; void ABC ();}; struct Cc:public C {long CC; void

ABC (); };

void main () {BB bb; bb. ABC (); CC CC; cc.cc = 10; }

First, perform BB above. ABC () but does not give a definition of bb::abc or B::ABC, so the above compilation passes, but the connection will fail. Second, CC is not implemented above. ABC (), but the connection will say cc::abc undefined to indicate the need for CC::ABC address here, why? Because an instance of CC is generated and cc::p f needs to be properly initialized in the compiler's default constructor for CC generation, it needs to be populated with CC::ABC addresses. Then, the definitions of each function are given.

void B::abc () {B = n} void C::abc () {C = 13;}

void Bb::abc () {BB = b = 10} void Cc::abc () {CC = n;

As above, for BB. ABC ();, equal to BB. BB::ABC (), although there are three bb::abc mapped elements, only one mapping element is of type void (BB::) (), which maps the BB::ABC address. Since BB::ABC is not decorated with virtual, it will be equivalent to BB. BB::ABC () rather than (bb.* (pf[0)) ();, BB will be 13. For CC. ABC (); Similarly, CC will be 13.

For ((b*) &BB)->abc (), because the left type is b*, it will be ((b*) &BB)->b::abc (), and since B::ABC is not defined as a virtual function, this is equivalent to ((b*) &BB)-&G T B::ABC ();, B will be 13. For ((c*) &cc)->abc (), the same will be ((c*) &cc)->c::abc (), but C::ABC is decorated as a virtual function, preceded by the C *pc = &cc; (pc->* (pc->pf[0]) ();。 This converts the CC to an instance of C, offset by 0. The address of the function is then indirectly obtained according to Pc->pf[0], and the cc::abc,c will be 10. Because CC is an instance of CC, the CC.PF is populated when it is constructed.

So as follows:

void (CC::* ccvf[]) () = {cc::abc, CC::BCD}; CC::CC () {cc.pf = &CCVF;}

thus causing pc->abc (), the result is called cc::abc rather than C::ABC, which is caused indirectly by the virtual result of the function address. Similarly, for (A *) &cc)->ABC () and ((*) &BB)->abc (), CC::ABC and BB::ABC are called respectively. Note, however, that (pc->* (Pc->pf[0])) (), the PC is of type c*, and pc->pf[0] returns cc::abc is void (CC::) (), and how does the above do an implicit type conversion of the instance? If you do not make the member that will cause the operation error. As mentioned above, let CCVF each member of the length of 8 bytes, the other 4 bytes to record the offset required. But most classes do not need to be offset (as the CC instance above is shifted to an instance of 0), and this method is a waste of resources. VC on this given the method is as follows, assuming the CC::ABC corresponding address is 6000, and assume the following label P address is 6000, and cc::a_thunk corresponding address is 5990.

void Cc::a_thunk (void *this)

{

This = ((char*) this) + diff;

P:

Normal code for CC::ABC

}

Therefore, the value of pc->pf[0] is 5990, not the 6000 corresponding to the CC::ABC. The diff above is the corresponding deviation

Move, for the above example, the diff should be 0, so the actual pc->pf[0] value is 6000 (because the offset is 0, no

Necessary is 5990). This method is called Thunk, which represents the short code that completes the simple function. For multiple inheritance, the following:

struct D:public A {long D;};

struct E:public B, public C, public D {long E; void ABC () {e = 10;}};

There will be three virtual function tables, because B, C, and D each carry a table of virtual functions (because they derive from a).

The results above are equivalent to:

struct E

{

void (E::* B_PF) (); Long b_a, B;

void (E::* C_PF) (); Long c_a, C;

void (E::* D_PF) (); Long d_a, D; Long e; void ABC () {e = 10;} E ();

void E_c_thunk_abc () {this = (e*) ((char*)-12); ABC (); }

void E_d_thunk_abc () {this = (e*) ((char*)-24); ABC (); }

};

void (E::* e_bvf[]) () = {e::abc, E::BCD};

void (E::* e_cvf[]) () = {e::e_c_thunk_abc, E::BCD};

void (E::* e_dvf[]) () = {e::e_d_thunk_abc, E::BCD};

E::e () {B_PF = E_BVF; C_PF = E_CVF; D_PF = E_DVF; }

Results e e; C *pc = &e; PC->ABC (); D *PD = &e; PD->ABC (), assuming the address of E is 3000, then the value of the PC is 3012,PD 3024. The result is that the value of the E_CVF,PD->PF is E_DVF, which solves the problem of PC->PF. Similarly, for the preceding virtual inheritance, when there are multiple virtual class tables in the class, such as:

struct A {};

struct b:virtual public a{}; struct c:virtual public a{}; struct d:virtual public a{};

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

This is that E will have three virtual class tables, and each virtual class table will be correctly initialized in the default constructor of E to guarantee the meaning of the virtual inheritance-indirectly obtained. The reason why the above virtual function table initialization is so complicated is only to ensure the correctness of the indirect access.

It should be noted that the E_BVF type is defined as void (e::* []) only because of the demo, which would like to write as much as possible on the code, and does not mean that the type of the virtual function can only be void (e::) (). The actual virtual function table is nothing more than an array of 4 bytes per element to record an address. It can therefore also be as follows:

struct A {virtual void ABC (); Virtual float ABC (double);

struct b:public A {void abc (); Float ABC (double);

then b b; A *pa = &b; PA->ABC (); b::abc that calls the type void (b::) (), PA->ABC (34), and B::ABC that invokes the type float (b::) (Double). They belong to overloaded functions, and even the same names are two different virtual functions. You should also pay attention to virtual and previous public, all just to provide the compiler with a syntax for some information, they give the information is specific to some special circumstances, rather than all in the use of the number of places are applicable, and therefore can not be the type of numbers. So virtual is not a type modifier, and it modifies a member function just to tell the compiler that where the member function is applied, it should indirectly obtain its address.

Why should we provide the concept of emptiness? That is, what is the meaning of virtual function and virtual inheritance? For space limitations, the discussion of their meaning will be given in the next chapter of this article, and the issues of polymorphism and instance replication are immediately illustrated.

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.