Virtual function: starting from scratch

xiaoxiao2021-03-06  105

The virtual function is related to the polymorphism, and the polygon is connected to inheritance. Therefore, this article is in the inheritance level. Didn't inherit, nothing to talk about.

Below is the understanding of the younger you're with a virtual function of C .

First, what is virtual function (if you don't know what the virtual function is, but if you want to know, then you should start here)

Simply put, those members of the Virtual keyword modified are virtual functions. The function of virtual functions, explains with professional terminology is to achieve polymorphism (polymorphism), and polymorphism is to separate the interface and implementation; interpretation with the language is to implement a common method, but different from individual differences Strategy. Let's take a simple code.

Class a {

PUBLIC:

Void Print () {cout << "this is a" << endl;}

}

Class B: Public a {

PUBLIC:

Void Print () {cout << "this is b" << endl;}

}

INT main () {// In order to distinguish in the future, I called MAIN1

A a a;

B B;

a.print ();

B.Print ();

}

Through the Print () interface of Class A and Class B, it can be seen that the two CLASs use different strategies due to individual differences, and the result of the output is also our expectations, which are this is a and this is B. But is this truly polymorphic? NO, the key is that the key is to operate the object with a pointer or reference to the base class. That now changed the code at the main ().

Int main () {// main2

A a a;

B B;

A * p1 = & a;

A * p2 = & b;

P1-> Print ();

P2-> Print ();

}

After running, look at the results, 哟呵, look back, the result is two this is A. The problem is here, P2 is clearly pointing to Class B object but is a print () function called Class A, which is not the result we expect, then solve this problem, you need to use virtual functions.

Class a {

PUBLIC:

Virtual void print () {cout << "this is a" << Endl;} // is now a virtual function

}

Class B: Public a {

PUBLIC:

Void Print () {cout << "this is b" << Endl;} / / here you need to add keyword Virtual?

}

There is no doubt that Class A's member function print () has become a virtual function, then Class B's print () is a virtual function? The answer is YES, we only need to set the base class to Virtual, and the corresponding function of its derived class will automatically become virtual functions. Therefore, Class B's print () has also become virtual functions. So do you need to use the Virtual keyword to modify it before the corresponding function of the derived class, that is your own problem.

Now re-run Main2 code, the result of the output is this is a and this is b.

Now digest, I am a simple summary, pointing to the base class, when operating its polymorphic class, call its corresponding function according to different class objects, this function is the virtual function.

Second, how the virtual function is done (if you haven't seen "Inside THE C Object Model" book, but anxiously, then you should start from here) how the virtual function is different due to the object And what is the corresponding function? Now let's analyze the virtual function. We define two classes first

Class A {// virtual function sample code

PUBLIC:

Virtual void fun () {cout << 1 << endl;}

Virtual void fun2 () {cout << 2 << endl;}

}

Class B: Public a {

PUBLIC:

Void fun () {cout << 3 << endl;

Void fun2 () {cout << 4 << endl;}

}

Since there are virtual functions in these two classes exists, the compiler will insert the data you don't know for them, and create a table separately. That data is called a VPTR pointer, pointing to that table. That table is called VTBL, each class has its own vtbl, the role of VTBL is to save the address of the virtual function in your own class, we can see the VTBL image as an array, and each element of this array is stored by virtual functions. Address, please see the picture

With the above figure, you can see that these two VTBLs are Class A and Class B services, respectively. Now there is this model, let's analyze the following code.

A * p = new a;

P-> fun ();

There is no doubt that I call A :: Fun (), but how is A :: Fun () how is called? Is it directly jumped to the code of the function like a normal function? NO, in fact, first is to remove the value of VPTR, this value is the address of the VTBL, then according to this value to vtbl, because the function called the function A :: fun () is the first virtual function, so Tet the VTBL The value in the first slot, this value is the address of a :: fun (), and finally calls this function. Now we can see that as long as VPTR is different, the poby VTBL is different, and the different VTBL is equipped with a virtual function address of the corresponding class, so this virtual function can complete its task.

And for Class A and Class B, where are their VPTR pointers stored? In fact, this pointer is placed in their respective instance objects. Since Class A and Class B have no data members, there is only one VPTR pointer in their instance objects. Through the above analysis, we now come to a piece of code to describe the simple model of this class with virtual functions.

#include

Using namespace std;

// Add above "virtual function sample code" to here

Int main () {

Void (* fun) (a *);

A * p = new b;

Long lvptraddr;

Memcpy (& lvptraddr, P, 4);

Memcpy (& fun, reinterpret_cast (lvptraddr), 4);

Fun (p);

Delete P;

System ("pause");

}

Compile to VC or DEV-C , see if it is output 3, if not, then the sun will definitely come out from the west. Now step by step

Void (* fun); this paragraph defines a function pointer name called FUN, and there is a parameter of a * type, this function pointer is used to save the function addressed from VTBL.

A * p = new b; I don't know much, forget it, don't explain this long lvptraddr; this long-type variable is used to save the value of VPTR

Memcpy (& lvptraddr, p, 4); before, only VPTR pointers in their instance objects, so we can reply to the 4bytes memory of the 4bytes memory to lvptraddr, so the 4bytes content that copies it is. VPTR's value, the address of VTBL

Now there is a VTBL address, then we will take out the contents of the first slot in VTBL now.

Memcpy (& fun, reinterpret_cast (lvptraddr), 4); Remove the contents of the first SLOT in VTBL and store it in the function pointer fun. It should be noted that the LVPTRADDR is the address of VTBL, but lvptradd is not a pointer, so we have to turn it into a pointer type.

Fun (p); here, the function in the function address just taken is called, that is, call B :: Fun (), maybe you find why there will be parameter P, and the active class member function is called, there will be A THIS pointer, this P is the THIS pointer, just in the general call, the compiler will automatically handle you, and here you need yourself.

Delete P; and System ("pause"); I don't know much, forget it, don't explain this

What should I do if I call B :: Fun2 ()? Then take the value in the second slot of VTBL.

Memcpy (& fun, reinterpret_cast (lvptraddr 4), 4); why is it add 4? Since a pointer is 4Bytes, add 4. Or Memcpy (& Fun, Reinterpret_Cast (lvptraddr) 1, 4); this is more in line with the usage of the array, because lvptraddr is turned into a long * type, so 1 is the length of SIZEOF (long)

Third, start with a code

#include

Using namespace std;

Class A {// virtual function sample code 2

PUBLIC:

Virtual void fun () {cout << "A :: fun" << endl;

Virtual void fun2 () {cout << "A :: fun2" << endl;}

}

Class B: Public a {

PUBLIC:

Void fun () {cout << "b :: fun" << endl;}

Void fun2 () {cout << "b :: fun2" << endl;

}; // end // virtual function sample code 2

Int main () {

Void (a :: * fun) (); // Defines a function pointer

A * p = new b;

Fun = & a :: fun;

(p -> * fun) ();

Fun = & a :: fun2;

(p -> * fun) ();

Delete P;

System ("pause");

}

Can you estimate the output result? If you estimate the result is A :: Fun and A :: Fun2, huh, please, congratulations, you have a circle. In fact, the real result is B :: Fun and B :: Fun2, if you don't want to see it. Give a prompt, & a :: fun and & a :: fun2 is the address that really gets the virtual function? First we go back to the second part, pass the paragraph actual code, get a method of obtaining virtual function addresses

#include

Using namespace std;

// Add the "virtual function sample code 2" here

Void CallVirtualFun (void * pthis, int index = 0) {

Void (* funptr) (void *);

Long lvptraddr;

Memcpy (& lvptraddr, PTHIS, 4);

Memcpy (& Funptr, ReinterPret_cast (lvptraddr) index, 4);

Funptr (pthis); // call

}

Int main () {

A * p = new b;

Callvirtualfun (p); // Call the virtual function P-> Fun ()

CallvirtualFun (p, 1); // Call the virtual function P-> FUN2 ()

System ("pause");

}

Now we have a "universal" CallVirtualFun method.

What is the connection between this general method and the third part starting? Contact great. Since A :: Fun () and A :: Fun2 () are virtual functions, & a :: fun and & a :: fun2 get the address of the function, but a piece of code for a virtual function address, we The image is imaged as the CallVirtualFun. When compiling, the compiler is compiled, which is similar to the CallVirtualFun. When you call the virtual function, it is actually called the code similar to the CallVirtualFun code. After this code, after the virtual function address, finally call the virtual function, This really guarantees the polymorphism. At the same time, everyone said that the virtual function is low, the reason is that before calling the virtual function, the code to obtain the virtual function address is called.

Last description: The code of this article can be compiled with VC6 and DEV-C 4.9.8, and there is no problem. Other compilers, the younger brother dare not guarantee. Among them, the ratio method can only be seen as a model because the low-level implementation of different compilers is different. For example, the THIS pointer, the DEV-C GCC is passed by pressing, as parameter passes, while the compiler of the VC is saved in ECX by the removal address. Therefore, these types of methods cannot be treated as a specific implementation.

PS: The level of younger brother is really limited, whether it is technology, or language, if there is any problem in the text, welcome a prawn and rookie friends to pointed out. Bright ~~

THE END

转载请注明原文地址:https://www.9cbs.com/read-124619.html

New Post(0)