I found this technique in the following link: http://www.codeproject.com/Tips/90875/Displaying-vtable-when-debugging
and there, he uses one helper variable
void (**vt)() = *(void (***)())ptr;
to help display the virtual function table.
But if I change it to
void (**vt)() = (void (**)())ptr;
it does not work as the previous one.
Could someone help me to explain what magic plays here, please?
Let's introduce a typedef for clarity.
typedef void (**thing)();
Then the first code is
thing vt = *(thing*) ptr;
and the second
thing vt = (thing) ptr;
That is, the first says "treat ptr as a pointer to a thing, and give me what it points at" and the second "treat ptr as a thing".
The difference should be obvious.
The reason it works at all is very specific to both the Visual C++ compiler and the Visual Studio debugger.
If the debugger doesn't know what type the object really is, it displays what it knows, which is that it's an instance of the base class, and it only knows how many entries the vtable of the base class has.
(You left out a crucial part here, which is to add the expected number of table entries to the item in the watch window. This makes the debugger display that memory as an array of as many elements as you say.)
So the trick consists of inventing a name for the vtable, and telling the debugger how many elements to display from the table.
The virtual table pointer in many C++ implementations is the first sizeof(void (**)())
bytes of the object. When you dereference that pointer you will get the starting address of the real virtual table. That is the meaning of the working code.
The cdecl
program might be of bit of a help here:
cdecl> explain void (***foo)()
declare foo as pointer to pointer to pointer to function returning void
cdecl> explain void (**foo)()
declare foo as pointer to pointer to function returning void
The first code casts the pointer to your object as a properly derefereancable pointer (pointer to pointer to pointer to function, void (***)()
), and then dereferences it to acquire the starting address to the virtual table, which is of a type void (**)()
(pointer to pointer to function), which points to the beginning of the virtual table that is of type void (*[])()
(array of pointer to function).
The second one just casts your pointer to object to a pointer to a pointer to a function returning void; the address stored in the variable vt is just the address of your object.
class Object {
public:
virtual void foo() {};
};
Object x;
// is approximately equivalent to
struct Object {
void (**_vtable)();
};
void _Object_foo(Object this) {
}
// this does not really compile, however,
// because a pointer mismatch
void (*_vtable_for_Object[])() = {
_Object_foo
};
Object y;
y._vtable = _vtable_for_Object;
Thus by having
Object *ptr = new Object();
// x now points to a block of memory,
// whose first bytes are void (**_vtable)()
// then
void (**vt)() = *(void (***)())ptr;
// is equivalent in our analogy to
void (**vt)() = ptr->_vtable;
// except that the C++ does not allow you to
// access the virtual table directly without
// the trickery of the former
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With