Currently most C++ compilers use vtables to realise polymorphism.
#include <iostream> struct Base { virtual void method() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; struct Child : public Base { virtual void method() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; struct GrandChild : public Child { virtual void method() { std::cout << __PRETTY_FUNCTION__ << std::endl; } }; int main() { Base* obj = new GrandChild; obj->method(); delete obj; return 0; }
$ g++ --version g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609 $ g++ main.cpp -g $ gdb a.out GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 (gdb) b 22 Breakpoint 1 at 0x4009eb: file main.cpp, line 22. (gdb) r virtual void GrandChild::method() Breakpoint 1, main () at main.cpp:22 22 delete obj; (gdb) p &obj $1 = (Base **) 0x7fffffffdcf8 (gdb) p obj $2 = (Base *) 0x614c20 (gdb) p *obj $3 = {_vptr.Base = 0x400bf8 <vtable for GrandChild+16>} (gdb) p sizeof(obj) $4 = 8 (gdb) x/2x obj x614c20: 0x00400bf8 0x00000000 (gdb) info symbol 0x400bf8 vtable for GrandChild + 16 in section .rodata of /<path_to_binary>/a.out (gdb) x/2x 0x00400bf8 0x400bf8 <_ZTV10GrandChild+16>: 0x00400aae 0x00000000 (gdb) info symbol 0x00400aae GrandChild::method() in section .text of /<path_to_binary>/a.out (gdb) info vtbl obj vtable for 'Base' @ 0x400bf8 (subobject @ 0x614c20): [0]: 0x400aae <GrandChild::method()>
The object (obj @ 0x614c20) contains a pointer to the vtable (@ 0x400bf8) of its class. The vtable contains the pointers to the actual methods (GrandChild::method() @ 0x400aae).
Stack Addr: 0x7fffffffdcf8 | Val: 0x614c20 (objPtr) | /--------------------/ Heap ↓ Addr: 0x614c20 | Val: 0x400bf8 (obj) | /--------------------/ Data ↓ Addr: 0x400bf8 | Val: 0x400aae (vtable of GrandChild) | /--------------------/ Text ↓ Addr: 0x400aae | Val: <asm code> (GrandChild::method())