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
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
COUT << "SIZE OF Object Point IS =" << sizeof (objPoint) << ENDL;
COUT << "Address of object point is =" << & objPoint << Endl;
CPOINT3D
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
Cout << (dword) static_cast
Cout << (dword) static_cast
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
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
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
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
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.