![]() |
![]() |
![]() |
![]() |
||
![]() |
![]() |
![]() |
|||
![]() |
![]() |
![]() |
|||
![]() |
|||||
Example of OOP (Win32 Asm) |
![]() |
![]() |
![]() |
![]() |
Polymorphic Object Layout |
|||
![]() |
![]() |
||
![]() |
Let's start with the description of how the object instance looks like in memory and how all parts interact with each other. The object itself is a structure with the first member as a pointer to the Virtual Methods Table (VMT). VMT is also a structure with the pointers to virtual functions. |
||
All pointers are DWORD values as dictated by Win32 platform. Object instance also can include any member variables which will follow the VMT address (VMT Offset on Pic. 1). Here I should mention that not all methods of the class should be mapped into VMT, but only the 'virtual' methods which can override the class behaviour. Virtual function call is slower than normal static call linked by LINKER. |
|||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Multiple Objects |
|||
![]() |
![]() |
||
![]() |
The VMT is nothing more than a storage of pointers to virtual functions. Since the class behaviour is the same for all the instances of objects of that class - we do not need to keep the VMT for each object. It will be the waste of room. Instead, (Pic. 2) we will have the single instance of VMT and all object instances will have the same VMT Offset value. |
||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Inheritance |
|||
![]() |
![]() |
||
![]() |
When the object gets derived from a base class - it can do the following: (Pic. 3)
|
||
At this point I have to mention a couple of important things. Both object structure and VMT structure must be altered only by APPENDING additional values AFTER the base class members. Easiest way to do it is to use nested structures. Also, even if the derived class has the same VMT as a base class - the VMT should be declared and defined anyway. Every class must have its own VMT for good maintainability of the project. If some classes will share VMT implementation and you have to modify just one of them you will be in trouble doing so, because modifying the shared VMT will modify behaviour of all classes attached to it. |
|||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Polymorphism |
|||
![]() |
![]() |
||
![]() |
To override the virtual method we simply have to replace the VMT entry at the moment of its initialization: (Pic. 4). The VMT initialization is best to be done at compile time, and not at run-time, to avoid performance related problems. Implementation of all parts will be discussed later on. |
||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Construction/Destruction |
|||
![]() |
![]() |
||
![]() |
Now it is time to talk about how we can initialize/release the object. VMT of every class will always contain both constructor and destructor functions as first two virtual entries: (Pic. 5). Every class has both of them - again - it may seem like an overhead, but in the long run you will see its benefits. Implementation of the constructor at the very least can provide the clearing of the member variables and pointers, etc. |
||
The fixed location of constructor and destructor virtual pointers allows certain coding benefits:
The body of the constructor of a derived class should look like this:
The body of the destructor of a derived class should look like this:
This coding convention will allow the chain of construction/destruction to be called by a single call to virtual entry in VMT of a class derived a few times. |
|||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Implementation |
|||
![]() |
![]() |
||
Now, enough with the theory!
|
|||
![]() |
![]() |
![]() |
![]() |