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())