The secret of the secret under ATL cloth

xiaoxiao2021-03-06  44

Introduction

In this series of tutorials, I want to discuss some of the internal works of ATL and the technology it use.

In the discussion, let's take a look at a program's memory distribution. First, write a simple program that does not have any data members, you can look at its memory structure.

Program 1.

#include using namespace std; curss class {}; int main () {class objclass; cout << "size of object is = << sizeof (objclass) << endl; cout <<" Address of Object IS = "<< & objclass << Endl; return 0;}

The output of this program is:

SIZE OF Object IS = 1ADDRESS OF Object IS = 0012FF7C

Now, if we add some data members to the class, then this type of size will be the sum of the size of each member. For templates, it is still like this:

Program 2.

#include using namespace std; template class cpoint {public: t m_x; t m_y;}; int main () {cpoint objPoint; cout << "Size of object is = << SIZEOF (ObjPoint) << Endl; cout << "address of object is =" << & objpoint << endl; return 0;

The output of the program is now:

SIZE OF Object IS = 8ADDRESS OF Object IS = 0012FF78

So, add inheritance to the program. Now let's inherit the Point3D class from the Point class, then take a look at the memory structure of the program:

Program 3.

#include using namespace std; template class cpoint {public: t m_x; t m_y;}; template CLASS CPOINT3D: PUBLIC CPOINT {public: t m_z;}; int main () {Cpoint objPoint; cout << "Size of object point is =" << sizeof (objPoint) << Endl; cout << "address of object point is =" << & objpoint << endl; cpoint3d < INT> objPoint3d; cout << "Size Object Point3d IS =" << SizeOf (ObjPoint3D) << endl; cout << "address of object point3d is =" << & objPoint3d << endl; return 0;} for:

Size of Object Point IS = 8ADDRESS OF Object Point IS = 0012FF78Size Of Object Point3d IS = 12Address of Object Point3d IS = 0012FF6C

This program demonstrates the memory structure of the derived class, which indicates that the memory occupied by the parties occupied by its own data member and the sum of the membership of its base class.

When the virtual function is added to this party, everything will become meaningful. Please see the following procedure:

Program 4.

#include using namespace std; class class {public: virtual void fun () {cout << "class :: fun" << endl;}}; int main () {class objclass; cout << "size of Class = "<< sizeof (objclass) << Endl; cout <<" address of class = "<< & objclass << endl; return 0;}

The output of the program is:

Size of class = 4Address of class = 0012FF7C

And, after we add more than one virtual function, it will become more interesting:

Program 5.

#include using namespace std; class class {public: virtual void fun1 () {cout << "class :: fun1" << Endl;} Virtual void fun2 () {cout << "class :: fun2" <

The output of the program is:

Address of Virtual Pointer 0012FF7cValue At Virtual Pointer 0046C060

The virtual function table pointer stores the address of a virtual function table. And, the virtual function table stores the address of all virtual functions in the class. In other words, the virtual function table is an array, and the element of this array is the address of the virtual function pointer. Let's take a look at this program below.

Program 10.

#include using namespace std; class class {virtual void fun () {cout << "class :: fun" << endl;}}; typedef void (* fun) (void); int main () {class Objclass; cout << "Address of Virtual Pointer << (int *) (& objclass 0) << Endl; cout <<" Value At Virtual Pointer IE Address of Virtual Table << (int *) * (int *) (& objclass 0) << Endl; cout << "Value at first entry of var" << (int *) * (int *) * (int *) (& objclass 0) << Endl; cout << Endl << "EXECUTING VIRTUAL FUNCTION" << endl << endl; fun pfun = (fun) * (int *) * (INT *) (& objclass 0); PFUN (); return 0;} Some in this program The indirect calls for type conversion are used, and the most important lines are:

Fun PFUN = (fun) * (int *) * (INT *) (& objclass 0);

Here, FUN is a function pointer type defined by TypeDef:

TypeDef void (* fun) (VOID);

Let us analyze this lengthy indirect call. (INT *) (& ObjClass 0) gives the address of the virtual function table pointer, this virtual function table pointer is the first entry of the class, and we convert it to int *. To get the value of this address, we need to use the indirect call operator (ie *), then convert it to int *, which is (int *) * (int *) (& objclass 0). This will give the first entry of the virtual function table. To get the value of this location, that is, get the address of the first virtual function in the class, we need to use the indirect call operator again, convert it to the appropriate function pointer type, so fun pfun = (fun) * (int *) * (INT *) (& objclass 0);

A value from the first entrance to the virtual function table is obtained, and the type conversion of FUN is deposited, and it is stored in PFUN.

So, how will it be more than one virtual function? Now we want to access the second virtual function in the virtual function table, please see the following procedure:

Program 11.

#include using namespace std; class class {virtual void f () {cout << "class :: f" << endl;} Virtual void g () {cout << "class :: g" << ENDL }}; int main () {class objclass; cout << "address of var * << (int *) (& objclass 0) << Endl; cout <<" Value At Virtual Pointer IE Address of Virtual Table " << (int *) * (INT *) << endl; cout << endl << "information about vTable" << "中文 b e e << Endl; cout <<" value at 1st entry of vTable < <(Int *) * (int *) (& objclass 0) 0) << endl; cout << "value at 2nd entry of vTable" << (int *) * ((int *) * (INT * ) * (INT *) (& objclass 0) 1) << Endl; return 0;} The output of the program is:

Address Of Virtual Pointer 0012ff7cValue At Virtual Pointer I. Address Of Virtual Table 0046C0ECINFORMATION ABOUT VTABLEVALUE AT 1ST Entry Of VTABLE 0040100AVALUE AT 2ND Entry of vTable 0040129E

So, there is a problem that appears naturally - how is the compiler know the length of the virtual function table? The answer is: The last entry of the virtual function table is NULL. You can consider this problem with the program.

Program 12.

#include using namespace std; class class {virtual void f () {cout << "class :: f" << endl;} Virtual void g () {cout << "class :: g" << ENDL }}; int main () {class objclass; cout << "address of var * << (int *) (& objclass 0) << Endl; cout <<" Value At Virtual Pointer IE Address of Virtual Table " << (int *) * (INT *) << endl; cout << endl << "information about vTable" << "中文 b e e << Endl; cout <<" value at 1st entry of vTable < <(Int *) * (int *) (& objclass 0) 0) << endl; cout << "value at 2nd entry of vTable" << (int *) * ((int *) * (INT * ) * (INT *) (& objclass 0) 1) << endl; cout << "value at 3rd entry of vTable" << (int *) * (int *) (& objclass 0 ) 2) << Endl; cout << "Value at 4th Entry of vTable" << (int *) * ((int *) * (& objclass 0) 3) << endl; return 0 The output of the program is:

Address of virtual pointer 0012FF7CValue at virtual pointer i.e. Address of virtual table 0046C134Information about VTableValue at 1st entry of VTable 0040100AValue at 2nd entry of VTable 0040129EValue at 3rd entry of VTable 00000000Value at 4th entry of VTable 73616C43

The output of this program demonstrates the last entry of the virtual function table as NULL. Let us call the virtual function with existing knowledge:

Program 13.

#include using namespace std; class class {virtual void f () {cout << "class :: f" << endl;} Virtual void g () {cout << "class :: g" << ENDL ;}}; typef void (* fun) (void); int main () {class objclass; fun pfun = null; // call the first virtual function PFUN = (fun) * (int *) * (int *) (& objclass 0) 0); PFUN (); // Call the second virtual function PFUN = (FUN) * (INT *) * (& objclass 0) 1); PFUN () Return 0;} The output of the program is:

Class :: fclass :: g

Now let's take a look at multiple inheritance. First look at the simplest situation inheritance:

Program 14.

#include using namespace std; class base1 {public: Virtual void f () {}}; class base2 {public: Virtual void f () {}}; class base3 {public: virtual void f () {}} Class Drive: Public Base1, Public Base2, Public Base3 {}; int main () {drive objDrive; cout << "size is =" << sizeof (objDrive) << endl; return 0;}

The output of the program is: Size IS = 12

This program demonstrates that when you inherit a class from multiple base classes, this derived class will have virtual function table pointers for all base classes.

So, what happens when derived class also has a virtual function table pointer? Let's take a look at the procedures below to understand the concept of multiple inheritance with virtual functions.

Program 15.

#include using namespace std; class base1 {virtual void f () {cout << "base1 :: f" << endl;} Virtual void g () {cout << "Base1 :: g" << ENDL }}; class base2 {Virtual void f () {cout << "base2 :: f" << endl;} Virtual void g () {cout << "base2 :: g" << endl;}}; class Base3 {Virtual Void f () {cout << "Base3 :: f" << Endl;} Virtual Void g () {cout << "Base3 :: g" << endl;}}; Class Drive: Public Base1, Public Base2, Public Base3 {public: Virtual Void fd () {cout << "drive :: fd" << Endl;} Virtual void gd () {cout << "drive :: gd" << Endl;}}; TypedEf void (* fun); int main () {drive objDrive; fun pfun = null; // Call Base1's first virtual function PFUN = (fun) * (int *) (int *) (INT *) (int *) & objDrive 0) 0); PFUN (); // Call Base1's second virtual function PFUN = (fun) * (int *) (INT *) & objDrive 0 ) 1); PFUN (); // Call Base2's first virtual function PFUN = (fun) * (int *) * (int *) & objDrive 1) 0); PFUN ( ); // Call the second virtual function PFUN = (fun) * (int *) * (int *) * (int *) * (int *) ((int *) * (int *) ((int *) * (int *); PFUN (); // Call Base3 First virtual function PFUN = (fun) * (int *) * (int *) & objDrive 2) 0); PFUN (); // Call Base3's second virtual function PFUN = ( Fun) * (int *) * (int *) & objDrive 2) 1); PFUN (); // Call the first virtual function PFUN = (fun) * (INT) * (INT) * *) * (int *) (INT *) & objDrive 0) 2); PFUN (); // Call the second virtual function pfun = (fun) * (INT *) ((int *) & objDrive 0) 3); PFUN (); return 0;} The output of the program is:

Base1 :: FBase1 :: GBase2 :: FBase2 :: FBase3 :: FBase3 :: FDRIVE :: FDDRIVE :: GD

We can get the offset of the derived class virtual table pointer by using Static_cast, see the following procedure: Program 16.

#include using namespace std; class base1 {public: Virtual void f () {}}; class base2 {public: Virtual void f () {}}; class base3 {public: virtual void f () {}} Class Drive: Public Base1, Public Base2, Public Base3 {}; // Arbitrary Non 0 Value, Because 0 by any number 0 # define Some_Value 1In () {cout << (dword) static_cast ((Drive *) Some_Value) -Some_Value << endl; cout << (dword) static_cast ((drive *) Some_Value) -Some_Value << endl; cout << (dword) static_cast DRIVE *) SOME_VALUE) --SOME_VALUE << Endl; return 0;}

ATL uses a macro OFFSTOFClass defined in atldef.h to do this, this macro is defined as:

#define offsetofclass (base, derived) / ((dword) (static_cast ((Derived *) _ ATL_PACKING)) -_ATL_Packing)

This macro returns the offset of the base class virtual function table pointer in the derived class object model, let us take a look at the following example:

Program 17.

#include #include using namespace std; class base1 {public: virtual void f () {}}; class base2 {public: Virtual void f () {}}; class base3 {public: Virtual Void f () {}}; class drive: public base1, public base2, public base3 {}; # define _tl_packing 8 # define offsetofclass (base, derived) / ((DWORD) (static_cast ((DERIVED *) _ATL_PACKING)) -_ATL_PACKING) int main () {cout << Offsetofclass (base1, drive) << endl; cout << Offsetofclass (base2, drive) << endl; cout << Offsetofclass (base3, drive) << ENDL; Return 0;}

The memory layout of the derived class is:

The output of the program is:

048

The output of this program demonstrates this macro returns a virtual function table pointer offset of the specified base class. In Don Box's "COM Nature", it uses a simple macro that you can modify this program, replace the ATL macro with the macro of the box. Program 18.

#include #include using namespace std; class base1 {public: virtual void f () {}}; class base2 {public: Virtual void f () {}}; class base3 {public: Virtual void f () {}}; class Drive: public Base1, public Base2, public Base3 {}; # define BASE_OFFSET (ClassName, BaseName) / (DWORD (static_cast (reinterpret_cast / (0x10000000)) ) - 0x10000000) int Main () {cout << Base_offset (drive, base1) << endl; cout << base_offset (drive, base2) << endl; cout << base_offset (drive, base3) << endl; return 0 }

The purpose and output of this procedure are identical to the previous program.

Now let's use this macro to make some special things. In fact, we can call the virtual function in the specified base class by getting the offset of the base class virtual function table pointer in the derived memory structure.

Program 19.

#include #include using namespace std; class base1 {public: Virtual void f () {cout << "base1 :: f () << endl;}}; class base2 {public: Virtual void f () {cout << "Base2 :: f ()" << endl;}}; class base3 {public: Virtual void f () {cout << "Base3 :: f () << end1 }}; class Drive: public Base1, public Base2, public Base3 {}; # define _ATL_PACKING 8 # define offsetofclass (base, derived) / ((DWORD) (static_cast ((derived *) _ ATL_PACKING)) -_ ATL_PACKING Int main () {drive D; void * pvoid = null; // Call Base1 function PVOID = (char *) & D OffsetOfClass (Base1, Drive); ((Base1 *) (pvoid)) -> f () ; // Call the function pvoid = (char *) & d offtofclass ((base2 *) (pvoid)) -> f (); // Call Base3 function (Translation: Original is BASE1) Pvoid ​​= (char *) & D OffsetOfClass (Base3, Drive); (Base3 *) (PVOID)) -> f (); return 0;} The output of the program is:

Base1 :: f () base2 :: f () base3 :: f ()

In this tutorial in this chapter, I tried to explain how the OFFSETOFCLASS macro in ATL. I hope to continue to explore other secrets in ATL in the next article.

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

New Post(0)