Pick up ATL's red cover (1)

zhaozj2021-02-17  55

Pick up ATL's red cover (1)

Introduction

In this series of chapters, I will discuss ATL internal working principles and skills.

Let's first discuss the procedure of the program in memory.

First look at the program 1: A simple program does not include any Data MEMBER. Analyze how its memory structure.

Program 1

#include

Using namespace std;

Class class {

}

Int main () {

Class Objclass;

COUT << "SIZE OF Object IS =" << sizeof (objclass) << ENDL;

COUT << "Address of object is =" << & objclass << Endl;

Return 0;

}

Program output:

SIZE OF Object IS = 1

Address of Object IS = 0012FF7C

If we add some Data Member in Class, the Class's size will be these Data Member size and, like the Template, look at the Template Class Point.

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;

}

Program output:

Size of Object IS = 8

Address of Object IS = 0012FF78

Next, let's take a look at the situation inheritance in the program. We derive a inherited Point3D from class Point. Then look at the program's memory structure

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 objPoint3d;

COUT << "Size Object Point3D IS =" << SizeOf (ObjPoint3D) << Endl; Cout << "Address of Object Point3D IS =" << & objPoint3D << Endl;

Return 0;

}

Program output:

Size of Object Point IS = 8

Address of Object Point IS = 0012FF78

Size of Object Point3D IS = 12

Address of Object Point3D IS = 0012FF6C

It is the size of the Data Member of the Data MEMBER in which the output result is seen by the output results.

Let's think about there is a virtual function in the program. Please see the next example

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;

}

Program output:

SIZE OF Class = 4

Address of class = 0012FF7C

What is the situation in how many virtual functions?

Program 5:

#include

Using namespace std;

Class class {

PUBLIC:

Virtual void fun1 () {cout << "class :: fun1" << endl;}

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

Virtual void fun3 () {cout << "class :: fun3" << endl;}

}

Int main () {

Class Objclass;

Cout << "SIZE OF Class =" << sizeof (objclass) << ENDL;

Cout << "Address of class =" << & objclass << ENDL;

Return 0;

}

The result of the program output is unchanged. Let us do some examples in order to deepen our understanding

Program 6:

#include

Using namespace std;

Class cpoint {

PUBLIC:

INT M_IX;

INT m_iy;

Virtual ~ cpoint () {};

}

Int main () {

Cpoint objPoint;

COUT << "SIZE OF Class =" << sizeof (objPoint) << Endl; cout << "address of class =" << & objpoint << endl;

Return 0;

}

Program output:

SIZE OF Class = 12

Address of class = 0012FF68

The program output results indicate that if we add a virtual function in the class, the memory size is the same as the INT type variable, for example, in Visual C it takes up 4 bytes. In the above example, the memory structure of the Class consists of an int type x and an int type y plus a pointer to the virtual function, but this pointer is in the start or end of the memory structure? Let us prove the following procedures

Program 7:

#include

Using namespace std;

Class cpoint {

PUBLIC:

INT M_IX;

INT m_iy;

CPOINT (const INT p_ix = 0, const INT p_iy = 0):

m_ix (p_ix), m_iy (p_iy) {

}

INT getx () const {

Return M_ix;

}

INT gety () const {

Return m_iy;

}

Virtual ~ cpoint () {};

}

Int main () {

Cpoint Objpoint (5, 10);

INT * PINT = (INT *) & objpoint;

* (PINT 0) = 100; // Want to change the value of X, let X = 100;

* (PINT 1) = 200; // Want to change the value of Y, let X = 200;

Cout << "x =" << objPoint.getx () << endl;

Cout << "y =" << objPoint.gety () << endl;

Return 0;

}

Pay attention to one of the lines of code:

INT * PINT = (INT *) & objpoint;

* (PINT 0) = 100; // Want to change the value of X, let X = 100;

* (PINT 1) = 200; // want to change the value of Y, let Y = 200;

The program output is as follows:

X = 200

Y = 10

Obviously this is not what we expected. Originally we want to assign 200 to x, but change the value of Y. This shows that it is not a member variable m_ix placed in the memory start position, but a virtual function ~ cpoint (), member m_ix and members m_iy are placed in the second and third positions, respectively. Now we change the two rows in the program.

INT * PINT = (INT *) & objpoint;

* (PINT 1) = 100; // Change the value of X, order x = 100;

* (PINT 2) = 200; // Change the value of Y, let Y = 200;

So we got the expected results, the entire procedure is as follows:

#include

Using namespace std;

Class cpoint {public:

INT M_IX;

INT m_iy;

CPOINT (const INT p_ix = 0, const INT p_iy = 0):

m_ix (p_ix), m_iy (p_iy) {

}

INT getx () const {

Return M_ix;

}

INT gety () const {

Return m_iy;

}

Virtual ~ cpoint () {};

}

Int main () {

Cpoint Objpoint (5, 10);

INT * PINT = (INT *) & objpoint;

* (PINT 1) = 100; // Want to Change The Value of X

* (PINT 2) = 200; // Want to Change The Value of Y

Cout << "x =" << objPoint.getx () << endl;

Cout << "y =" << objPoint.gety () << endl;

Return 0;

}

Program output:

X = 100

Y = 200

These results show: Whether we are declaring the Class's virtual function in the program, the system allocates the start position of the memory to the "virtual pointer"

Now the new problem is generated, what is the content stored in the "virtual pointer". Let's take a look at the following procedure to see if you can get inspiration

Program 9:

#include

Using namespace std;

Class class {

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

}

Int main () {

Class Objclass;

COUT << "Address of Virtual Pointer << (int *) (& objclass 0) << endl;

Cout << "Value At Virtual Pointer" << (int *) * (INT *) (& objclass 0) << ENDL;

Return 0;

}

The program output is as follows:

Address of Virtual Pointer 0012FF7C

Value at Virtual Pointer 0046C060

This "virtual pointer" content is the address of a virtual function table, and the address of all the virtual functions in the Class table is placed in the virtual function table. Let's take a look at the procedure 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 I. Address of Virtual Table"

<< (int *) * (INT *) << Endl; cout << "Value at first entry of varry table"

<< (int *) * (int *) * (INT *) (& objclass 0) << ENDL;

COUT << Endl << "Executing Virtual Function" << Endl << Endl;

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

PFUN ();

Return 0;

}

There are some unusual type conversions in this program, and the most important thing is:

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

Here FUN is defined as a function pointer: typedef void (* fun) (Void);

Let's analyze: (int *) (& objclass 0) Get the address of the category of the category and convert the address into an int pointer type, in order to get the content of the virtual pointer, we add a pointer * and convert it to INT pointer type, becomes as follows: (int *) * (int *) (& objclass 0), then we get the entrance address of the virtual function table, to get the value of the address we will add pointer to the front, then Convert it into a function pointer as follows:

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

EN: Means Get The Value from the first entry of the Virtual Table and Store IT INTO The Fun Type.

PFUN has become a pointer function to virtual functions

If there is more than one virtual function in the class? Let's take a look at how to get the second member of the virtual function table, look at the procedure below.

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 Virtual Pointer << (int *) (& objclass 0) << endl;

Cout << "Value At Virtual Pointer I. Address of Virtual Table"

<< (int *) * (INT *) (& objclass 0) << endl;

Cout << Endl << "Information about vTable" << Endl << Endl;

Cout << "Value At 1st Entry Of VTABLE"

<< (int *) * (int *) (& objclass 0) 0) << Endl; cout << "value at 2nd entry of vTable"

<< (int *) * (INT *) (& objclass 0) 1) << ENDL;

Return 0;

}

The program output is as follows:

Address of Virtual Pointer 0012FF7C

Value at Virtual Pointer I.E. Address of Virtual Table 0046C0EC

Information About VTABLE

Value at 1st Entry of vtable 0040100A

Value at 2nd entry of vTable 0040129E

Now the new problem is coming, how do the editor know the length of the virtual function table? The answer is that the virtual function table ends with NULL, we will change the program to prove this conclusion.

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 Virtual Pointer << (int *) (& objclass 0) << endl;

Cout << "Value At Virtual Pointer I. Address of Virtual Table"

<< (int *) * (INT *) (& objclass 0) << endl;

Cout << Endl << "Information about vTable" << Endl << Endl;

Cout << "Value At 1st Entry Of VTABLE"

<< (int *) * (INT *) (INT *) (& objclass 0) 0) << ENDL;

Cout << "Value At 2nd Entry of VTABLE"

<< (int *) * (INT *) (& objclass 0) 1) << ENDL;

Return 0;

}

Program output:

Address of Virtual Pointer 0012FF7C

Value at Virtual Pointer I.E. Address of Virtual Table 0046C134

Information About VTABLE

Value at 1st Entry of vtable 0040100A

Value at 2nd entry of vTable 0040129E

Value at 3rd Entry of vtable 00000000

Value at 4th Entry of VTable 73616C43

The output results indicate that the virtual function table is ended with NULL. Here we see how to call the virtual function from the knowledge learned.

Program 13: #include

Using namespace std;

Class class {

Virtual void f () {cout << "Class :: f" << endl;

Virtual void g () {cout << "class :: g" << endl;

}

TypeDef void (* fun) (VOID);

Int main () {

Class Objclass;

Fun Pfun = NULL;

// Calling 1st Virtual FUNCTION

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

PFUN ();

// Calling 2nd Virtual Function

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

PFUN ();

Return 0;

}

Program output:

Class :: f

Class :: g

Let us now look at the example of multiple inheritance situations. First come to a simple.

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;

}

Program output:

Size is = 12

The program output results indicate that if there is n base classes in the DRIVE class, when the editor is allocated, there will be N virtual pointer points to the virtual function table of each base class.

If Drive Lei itself has a virtual function, how is the memory allocation? Let us look at the following procedures to better understand how to inherit.

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) (VOID);

Int main () {

Drive objdrive;

Fun Pfun = NULL;

// Calling 1st Virtual Function Of Base1

PFUN = (fun) * (INT *) (INT *) (INT *) & objDrive 0) 0);

PFUN ();

// Calling 2nd Virtual Function Of Base1

PFUN = (fun) * (INT *) (INT * & ObjDrive 0) 1);

PFUN ();

// Calling 1st Virtual Function Of Base2

PFUN = (fun) * (INT *) (INT *) & objDrive 1) 0);

PFUN ();

// Calling 2nd Virtual Function Of Base2

PFUN = (fun) * (INT *) (INT *) & objDrive 1) 1);

PFUN ();

// Calling 1st Virtual Function Of Base3

PFUN = (fun) * (INT *) (INT *) & objDrive 2) 0);

PFUN ();

// Calling 2nd Virtual Function Of Base3

PFUN = (fun) * ((int *) * (int *) & objDrive 2) 1);

PFUN ();

// Calling 1st Virtual Function Of Drive

PFUN = (fun) * (INT *) (INT *) & objDrive 0) 2);

PFUN ();

// Calling 2nd Virtual Function of Drive

PFUN = (fun) * (INT *) (INT * & ObjDrive 0) 3);

PFUN ();

Return 0;

}

Program output:

Base1 :: f

Base1 :: g

Base2 :: f

Base2 :: f

Base3 :: f

Base3 :: f

Drive :: fd

Drive :: GD

From the program output, we see the virtual function of the DRIVE class is placed behind the virtual function points to the first virtual pointer.

We can also use Static_cast to get the relative address of the DRIVE category, let's take a look at the procedure below to better understand.

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 {

}

// Any Non Zero Value Because Multiply Zero with Any No is Zero # Define Some_Value 1

Int main () {

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;

}

In the ATLDEF.h header file, use a macro variable called offsetofclass to do this, the macro is as follows:

#define offsetofClass (Base, Derived) /

((DWORD) (static_cast ((Derived *) _ ATL_PACKING)) -_ atl_packing)

This macro variable returns the relative displacement of the virtual pointer of the DRIVE base class, let us look at one example below.

#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 _atl_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 allocation of class Drive is as follows

Program output:

0

4

8

Program Output Description This macro's return value is the relative position of the virtual pointer of the base class, in the "Essential CoM" book written in Don Box, using a simple macro to complete these work. We make a small change to show the macro of the box.

#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 output of the program is the same as the original program.

Let us use these macros to practice some exercises, in fact, we call these virtual functions by obtaining relative displacement of these virtual functions.

#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 ()" << endl;}

}

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 Function Of Base1

Pvoid ​​= (char *) & D OffsetOfClass (Base1, Drive);

((BASE1 *) (pvoid)) -> f ();

// Call Function of Base2

Pvoid ​​= (char *) & D OffsetOfClass (Base2, Drive);

((Base2 *) (PVOID)) -> f ();

// Call Function Of Base1

Pvoid ​​= (char *) & D OffsetOfClass (Base3, Drive);

((Base3 *) (PVOID)) -> f ();

Return 0;

}

Program output:

Base1 :: f ()

Base2 :: f ()

Base3 :: f ()

In this section, I tried to tell the working principle of the ATL in the macro versionetofclass, I plan to analyze the other characteristics of ATL in my other chapters.

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

New Post(0)