Secret under ATL Pradesh (2)

zhaozj2021-02-16  80

Author: Zeeshan Amjad Original link: http: //www.codeproject.com/atl/atl_underthehood_2.asp described in the tutorial in this series, I will discuss some of the inner workings of ATL and technology it uses, which are A second article of the series. Now let us explore some more interesting information behind the virtual function. In order to maintain consistent with the above, in the discussion of this article, I will use the same order, and the serial number of the program starts from 20. Let's take a look at the procedure below: Program 20. # include usingspace std; class base {public: virtual void fun () {cout << "base :: fun" <<} void show () { Fun ();}}; class drive: public base {public: Virtual void fun () {cout << "drive :: fun" << endl;}}; int main () {drive d; d.show () Return 0;} The output of the program is: Drive :: Fun This program clearly demonstrates how the function of the base class is to call the virtual function of the derived class. This technology is used in different frameworks, such as MFC and design modes (such as Template Design Pattern). Now you can modify this program to see its behavior, I will call the virtual function in the constructor of the base class, not ordinary member functions. Program 21. # include using namespace std; class base {public: base () {fun ();} Virtual void fun () {cout << "Base :: fun" <<}}; : Public base {public: virtual void fun () {cout << "drive :: fun" << endl;}}; int main () {drive d; return 0;} The output of the program: Base :: fun The program shows that we cannot call the virtual function of the derived class in the constructor of the base class. Ok, let's take a look at what I did in the bottom of the cloth. I will print the pointer value among these constructors. For the sake of simplicity, I removed other functions in the class.

Program 22. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "this pointer =" << (int *) this << Endl COUT << Endl;} Virtual void f () {cout << "Base :: f" << end1;}}; class drive: public base {public: drive () {cout << "in drive" << Endl; cout << "this pointer =" << (int *) this << Endl; cout << endl;} Virtual void f () {cout << "drive :: f" << endl;}}; int Main () {drive d; cout << "in main" << Endl; cout << (int *) & d << Endl; Return 0;} The output of the program: in basethis pointer = 0012FF7CIN drivethis Pointer = 0012FF7CIN MAIN0012FF7C In addition, only one object is present in the entire memory location. Then let's print this pointer to the value, that is, the value of the pointer VPTR pointing to the virtual function table, the address of the VTable.

Program 23. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" << (int *) this << Endl COUT << "Address of vTable =" << (int *) * (int *) this << Endl; cout << "value at vtable =" << (int *) * (int *) * (int *) This << Endl; cout << endl;} Virtual void f1 () {cout << "Base :: f1" << end1;}}; class drive: public base {public: drive () {cout << " In Drive << Endl; cout << "Virtual Pointer =" << (int *) this << Endl; cout << "address of vtable = << (int *) * (int *) this << ENDL COUT << "Value AT vTable =" << (int *) * (int *) * (int *) this << Endl; cout << endl;} Virtual void f1 () {cout << "Drive :: f2 "<< endl;}}; int main () {Drive d; return 0;} output program is: in BaseVirtual Pointer = 0012FF7CAddress of Vtable = 0046C08CValue at Vtable = 004010F0In DriveVirtual Pointer = 0012FF7CAddress of Vtable = 0046C07CValue at Vtable = 00401217 This program demonstrates different virtual functions in the base class and derived class. site. In order to better understand this problem, let us deepen the inheritance level and add a MostDrive class inherited in the Drive class and build an object.

Program 24. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" << (int *) this << Endl COUT << "Address of vTable =" << (int *) * (int *) this << Endl; cout << "value at vtable =" << (int *) * (int *) * (int *) This << Endl; cout << endl;} Virtual void f1 () {cout << "Base :: f1" << end1;}}; class drive: public base {public: drive () {cout << " In Drive << Endl; cout << "Virtual Pointer =" << (int *) this << Endl; cout << "address of vtable = << (int *) * (int *) this << ENDL COUT << "Value AT vTable =" << (int *) * (int *) * (int *) this << Endl; cout << endl;} Virtual void f1 () {cout << "Drive :: F2 "<< Endl;}}; Class MostDrive: PUBLIC DRIVE {public: MostDrive () {cout <<" in MostDrive "<< Endl; cout <<" Virtual Pointer = << (int *) this << Endl COUT << "Address of vTable =" << (int *) * (int *) this << Endl; cout << "Value A T vTable = "<< (int *) * (int *) this << endl; cout << endl;} Virtual void f1 () {cout <<" MostDrive :: F2 "<< }}; int main () {MOSTDRIVE D; RETURN 0;} The output of the program: in basevirtual pointer = 0012ff7caddress of vTable = 0046c0A0Value at vtable = 004010f5in DriveVirtual Pointer =

0012FF7CAddress of Vtable = 0046C090Value at Vtable = 00401221In MostDriveVirtual Pointer = 0012FF7CAddress of Vtable = 0046C080Value at Vtable = 00401186 This procedure demonstrates the virtual function table pointer in the constructor initialization process in each class. In this way, the address of the virtual function table in each class constructor is different, and the main function uses the most bottom derived class in the inheritance chain to create an object. Now you can look at the location of the constructor of each class in the virtual function table, you can store the first entry in the virtual function table into a function pointer and try running it.

Program 25. # include using namespace std; typedef void (* fun) (); class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" < <(Int *) this << Endl; cout << "address of vtable =" << (int *) * (int *) this << Endl; cout << "value at vtable =" << (INT *) * (int *) * (int *) this << Endl; fun pfun = (fun) * (int *) * (int *) this; pfun (); couid f1 ()} Virtual void f1 ()} Virtual Void F1 ()} << "Base :: F1" << Endl;}}; Class Drive: public base {public: drive () {cout << "in drive" << endl; cout << "Virtual Pointer = << (int *) ^ << endl; cout << "address of vtable =" << (int *) * (int *) this << Endl; cout << "value at vtable = << (int *) * (int *) * *) * (int *) this << Endl; fun pfun = (fun) * (int *) * (int *) this; pfun (); couid f1 ()} Virtual Void F1 () {cout << " Drive :: f1 "<< endl;}}; class MostDrive: public drive {public: MostDrive () {cout <<" in MostDrive "<< COUT << "Virtual Pointer =" << (int *) this << endl; cout << "address of vtable =" << (int *) * (int *) this << endl; cout << "Value At VTABLE = "<< (int *) * (int *) * (int *) this << Endl; fun pfun = (fun) * (int *) * (int *) this; pfun (); cout << Endl } Virtual void f1 () {cout << "

MostDrive :: f1 "<< endl;}}; int main () {MostDrive d; return 0;} output program is: In BaseVirtual Pointer = 0012FF7CAddress of Vtable = 0046C098Value at Vtable = 004010F5Base :: f1In DriveVirtual Pointer = 0012FF7CAddress of vtable = 0046C088Value at vtable = 00401221Drive :: f1In MostDriveVirtual Pointer = 0012FF7CAddress of vtable = 0046C078Value at vtable = 00401186MostDrive :: f1 this constructor procedure demonstrates how each class with their virtual functions to populate the virtual function table Each entrance. So, the Base class uses the Base class's virtual function address to fill your own virtual function table, when the DRIVE class constructor is executed, another virtual function table is created, and store its own virtual function address. Now You will find that in the case of multiple virtual functions in the base class, derived classes do not completely rewrite them.

Program 26. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" << (int *) this << Endl COUT << "Address of vtable =" << (int *) * (int *) this << Endl; cout << "Value AT VTable 1st Entry = << (int *) * ((int *) * (int *) THIS 0) << Endl; cout << "Value AT vTable 2nd entry =" << (int *) * (int *) * (int *) THIS 1) << Endl; cout < <"Value AT VTABLE 3rd Entry =" << (INT *) * (INT *) * (INT *) THIS 2) << Endl; Cout << Endl;} Virtual Void F1 () {COUT << " Base :: f1 "<< Endl;} Virtual Void F2 () {cout <<" Base :: F2 "<< endl;}}; class drive: public base}}; Class Drive: public base {public: drive () {cout <<" in drive << endl; cout << "Virtual Pointer =" << (int *) this << Endl; cout << "address of vTable = << (int *) * (int *) this << endl; cout << "Value AT VTable 1st Entry =" << (int *) * (INT *) * (INT *) THIS 0) << Endl; cout << "value at vtable 2nd entry = << (int *) *) * (INT *) * (int *) this 1) << Endl; cout << "Value AT VTable 3rd Entry =" << (int *) * ((int *) * (int *) this 2) << Endl; couid f1 ()} Virtual Void F1 () { COUT << "drive :: f1" << endl;}}; int main () {drive d; return 0;} The output of the program: in basevirtual pointer = 0012ff7caddress of vTable =

0046C0E0Value at Vtable 1st entry = 004010F0Value at Vtable 2nd entry = 00401145Value at Vtable 3rd entry = 00000000In DriveVirtual Pointer = 0012FF7CAddress of this output Vtable = 0046C0C8Value at Vtable 1st entry = 0040121CValue at Vtable 2nd entry = 00401145Value at Vtable 3rd entry = 00000000 program show The virtual function of the base class is not overwritten in the derived class, then, the constructor of the derived class does not do anything to the entrance of the virtual function. So now, let me invite the pure virtual function to join this game, then take a look at its behavior.

Please see the following procedures: program 27. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "Virtual Pointer =" << (int *) @< endl; cout << "address of vTable =" << (int *) * (int *) this << endl; cout << "value at vTable 1st entry = << (int *) * ((int *) * (int *) this 0) << Endl; cout << "value at vtable 2nd entry = << (int *) * ((int *) * (int *) THIS 1) << Endl; cout << Endl;} Virtual Void F1 () = 0; Virtual Void F2 () = 0;}; Class Drive: Public Base {public: drive () {cout << "in driving" << Endl COUT << "Virtual Pointer =" << (int *) this << endl; cout << "address of vtable = << (int *) * (int *) this << Endl; cout <<" Value AT vTable 1st Entry = "<< (int *) * (INT *) * (int *) this 0) << Endl; cout <<" Value AT VTABLE 2nd entry = << (int *) * ( (int *) * (int *) this 1) << endl; cout << endl;} Virtual void f1 () {cout << "drive :: f1" << Endl;} Virtual Void f2 () {cout << "DRIVE: : f2 "<< endl;}}; int main () {drive d; return 0;} In Debug and Release mode, the output of the program is different.

Here is the output debug mode: In BaseVirtual Pointer = 0012FF7CAddress of Vtable = 0046C0BCValue at Vtable 1st entry = 00420CB0Value at Vtable 2nd entry = 00420CB0In DriveVirtual Pointer = 0012FF7CAddress of Vtable 0046C0A4Value at Vtable 1st entry = 00401212Value at Vtable 2nd entry = 0040128F or less as the release output mode: in BaseVirtual Pointer = 0012FF80Address of Vtable = 0042115CValue at Vtable 1st entry = 0041245DValue at Vtable 2nd entry = 0041245DIn DriveVirtual Pointer = 0012FF80Address of Vtable = 00421154Value at Vtable 1st entry = 00401310Value at Vtable 2nd entry = 00401380 in order to better Improve this principle, we need to make a little change to the program, and try to use the function pointer to call the virtual function.

Program 28. # include using namespace std; typedef void (* fun) (); class base {public: base () {cout << "in base" << Endl; cout << "Virtual Pointer =" < <(Int *) this << endl; cout << "address of vtable = << (int *) * (int *) this << endl; cout <<" value at vtable 1st entry = << (int *) *) * (int *) * (int *) this 0) << Endl; cout << "value at vtable 2nd entry = << (int *) * (int *) * (int *) this 1) << endl; // Try to perform the first virtual function fun pfun = (fun) * (int *) * (int *) THIS 0); pfun (); cout << endl;} Virtual void F1 () = 0; Virtual Void F2 () = 0;}; Class Drive: public base {public: drive () {cout << "in driving" << endl; cout << "Virtual Pointer =" << INT *) This << endl; cout << "address of vtable = << (int *) * (int *) this << Endl; cout <<" value at vtable 1st entry = << (int *) * (INT *) * (int *) this 0) << endl; cout << "value at vtable 2nd entry = << (int *) * (INT *) * (int *) this 1 << Endl; cout << Endl;} Virtual void f1 () {cout << "drive :: f1" << endl;} Virtual void f2 () {cout << "drive :: f2" <<}}; int main ()} Drive d; Return 0;} The behavior of the program is still different in debug and release mode.

In Debug mode, it will display a runtime error dialog: and, when you press the "Ignore" button, it displays the following dialog box: and run in Release mode, it will only be on the console window output an error message: in BaseVirtual Pointer = 0012FF80Address of Vtable = 0042115CValue at Vtable 1st entry = 0041245DValue at Vtable 2nd entry = 0041245Druntime error R6025- pure virtual function call then R6025 what is here? It is defined in the CMSGS.h header file, which defines all the error messages of all C Run Time Library. #define _rt_purevirt_txt "R6025" - Pure Virtual Function Call "EOL In fact, when we define the pure virtual function, the compiler places a function address called _pureCall's C Run Time Library. This function is defined in purevirt.c, its prototype is as follows: void __cdecl _purecall (void); // Translation: Original This no number This function can directly call this function directly to achieve the same effect, please see below This applet: program 29.int main () {_purecall (); return 0;} This program is the same in the DEBUG mode and the output of Release mode. In order to better understand this problem, let us make a deeper inheritance chain, and inherit a class from the DRIVE class to see the effect.

Program 30. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" << (int *) this << Endl COUT << "Address of vtable =" << (int *) * (int *) this << Endl; cout << "Value AT VTable 1st Entry = << (int *) * ((int *) * (int *) THIS 0) << Endl; cout << "Value AT vTable 2nd entry =" << (int *) * (int *) * (int *) THIS 1) << Endl; cout <

Program 31. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "Virtual Pointer =" << (int *) this << Endl COUT << "Address of vtable =" << (int *) * (int *) this << Endl; cout << "Value AT VTable 1st Entry = << (int *) * ((int *) * (int *) THIS 0) << Endl; cout << "Value AT vTable 2nd entry =" << (int *) * (int *) * (int *) THIS 1) << Endl; cout <

(INT *) * (INT *) * (INT *) THIS 1) << endl; cout << Endl;} Virtual Void F1 () {cout << "MostDrive :: f1" << Endl;} Virtual Void F2 () {cout << "MostDrive :: F2" << endl;}}; int main () {MostDrive D; Return 0;} The output is: in basevirtual pointer = 0012FF7CADDRESS OF VTABLE = 0046C0ccValue AT VTABLE 1ST entry = 00420E60Value at Vtable 2nd entry = 00420E60In DriveVirtual Pointer = 0012FF7CAddress of Vtable = 0046C0B4Value at Vtable 1st entry = 00420E60Value at Vtable 2nd entry = 00420E60In MostDriveVirtual Pointer = 0012FF7CAddress of Vtable = 0046C0B4Value at Vtable 1st entry = 00420E60Value at Vtable 2nd entry = 00420E60 this The program has another result, which is the DRIVE and MOSTDRIVE classes have the same virtual function table address, but the base class is different. In fact, this is because we don't have the use of the __DECLSPEC (NOVTABLE) attribute for the Base class. Now, let's use the same properties to the inherited Drive class, that is, __DECLSPEC (NOVTABLE).

Program 32. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" << (int *) {= COUT << "Address of vtable =" << (int *) * (int *) this << Endl; cout << "Value AT VTable 1st Entry = << (int *) * ((int *) * (int *) THIS 0) << Endl; cout << "Value AT vTable 2nd entry =" << (int *) * (int *) * (int *) THIS 1) << Endl; cout <

"<< (int *) * (int *) * (int *) this 1) << endl; cout << Endl;} Virtual void f1 () {cout <<" MostDrive :: f1 "<< endl Virtual Void F2 () {cout << "MostDrive :: F2" << endl;}}; int main () {MOSTDRIVE D; RETURN 0;} Now, the program's output is: in basevirtual pointer = 0012FF7CADDRESS OF VTABLE = 0046C0C0Value at Vtable 1st entry = 00420E50Value at Vtable 2nd entry = 00420E50In DriveVirtual Pointer = 0012FF7CAddress of Vtable = 0046C0C0Value at Vtable 1st entry = 00420E50Value at Vtable 2nd entry = 00420E50In MostDriveVirtual Pointer = 0012FF7CAddress of Vtable = 0046C0C0Value at Vtable 1st entry = 00420E50Value at Vtable 2nd entry = 00420E50 In MSDN, the interpretation of __declspec (novTable) is: it should be used in pure hypothesis. So, let's make an experiment to better understand this meaning.

Program 33. # include using namespace std; class base {public: base () {cout << "in base" << endl; cout << "virtual pointer =" << (int *) this << Endl COUT << "Address of vtable =" << (int *) * (int *) this << Endl; cout << "Value AT VTable 1st Entry = << (int *) * ((int *) * (int *) THIS 0) << Endl; cout << "Value AT vTable 2nd entry =" << (int *) * (int *) * (int *) THIS 1) << Endl; cout <

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

New Post(0)