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
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
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
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
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 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 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 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 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 Class :: fclass :: g Now let's take a look at multiple inheritance. First look at the simplest situation inheritance: Program 14. #include 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 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 ATL uses a macro OFFSTOFClass defined in atldef.h to do this, this macro is defined as: #define offsetofclass (base, derived) / ((dword) (static_cast 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 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 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 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.