Source code
#include <memory> int main() { std::shared_ptr<int> sPtr1 = std::make_shared<int>(42); std::shared_ptr<int> sPtr2 = sPtr1; return 0; }
$ g++ --version g++ (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0 $ g++ main.cpp -std=c++11 -g
Debugging
$ gdb a.out (gdb) b 7 (gdb) r Breakpoint 1, main () at main.cpp:7 7 return 0; // Shared pointer with Pretty printer (gdb) enable pretty-printer 163 printers enabled 163 of 163 printers enabled (gdb) p sPtr1 $1 = std::shared_ptr<int> (use count 2, weak count 0) = {get() = 0x615c30} (gdb) p sPtr2 $2 = std::shared_ptr<int> (use count 2, weak count 0) = {get() = 0x615c30} // Shared pointer without Pretty printer (gdb) disable pretty-printer 163 printers disabled 0 of 163 printers enabled (gdb) p sPtr1 $3 = {<std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>> = {<std::__shared_ptr_access<int, (__gnu_cxx::_Lock_policy)2, false, false>> = {<No data fields>}, _M_ptr = 0x615c30, _M_refcount = {_M_pi = 0x615c20}}, <No data fields>} (gdb) p sPtr2 $4 = {<std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>> = {<std::__shared_ptr_access<int, (__gnu_cxx::_Lock_policy)2, false, false>> = {<No data fields>}, _M_ptr = 0x615c30, _M_refcount = {_M_pi = 0x615c20}}, <No data fields>} (gdb) p &sPtr1 $5 = (std::shared_ptr<int> *) 0x7fffffffdca0 (gdb) p sizeof(sPtr1) $6 = 16 (gdb) p &sPtr2 $7 = (std::shared_ptr<int> *) 0x7fffffffdcb0 (gdb) p sizeof(sPtr2) $8 = 16 // Object (The same behind sPtr1 and sPtr2) (gdb) p *sPtr1._M_ptr $9 = 42 (gdb) p sizeof(*sPtr1._M_ptr) $10 = 4 // Control block (The same behind sPtr1 and sPtr2) (gdb) p *sPtr1._M_refcount._M_pi $11 = {<std::_Mutex_base<(__gnu_cxx::_Lock_policy)2>> = {<No data fields>}, _vptr._Sp_counted_base = 0x401970 <vtable for std::_Sp_counted_ptr_inplace<int, std::allocator<int>, (__gnu_cxx::_Lock_policy)2>+16>, _M_use_count = 2, _M_weak_count = 1} (gdb) p sizeof(*sPtr1._M_refcount._M_pi) $12 = 16 // Both shared_ptr (Stack) (gdb) x/4x 0x7fffffffdca0 0x7fffffffdca0: 0x00615c30 0x00000000 0x00615c20 0x00000000 (gdb) x/4x 0x7fffffffdcb0 0x7fffffffdcb0: 0x00615c30 0x00000000 0x00615c20 0x00000000 // Object (Heap) (gdb) x/d 0x00615c30 0x615c30: 42 // Control block (Heap) (gdb) x/4x 0x00615c20 0x615c20: 0x00401970 0x00000000 0x00000002 0x00000001 (gdb) info symbol 0x401970 vtable for std::_Sp_counted_ptr_inplace<int, std::allocator<int>, (__gnu_cxx::_Lock_policy)2> + 16 in section .rodata of /<path_to_binary>/a.out
Visual result
|----------------------| | shared_ptr sPtr1 | |----------------------| |Pointer to the Object |---|-------> Object | | | (Int value 42) |Pointer to the Ctr blk|---|---| |----------------------| | |---> Ctl block | | (Pointer to allocator, deleter, ...) |----------------------| | | (Shared reference counter) | shared_ptr sPtr2 | | | (Weak reference counter + offset 1) |----------------------| | | |Pointer to the Object |---| | | | | |Pointer to the Ctr blk|-------| |----------------------|
Stack (Frame 0) --------------------- 0x7fffffffdcb8: 0x00615c20 0x00000000 (Ctl block) sPtr2 0x7fffffffdcb0: 0x00615c30 0x00000000 (Object) 0x7fffffffdca8: 0x00615c20 0x00000000 (Ctl block) sPtr1 0x7fffffffdca0: 0x00615c30 0x00000000 (Object) --------------------- ↓ ↑ Heap --------------------- Obj 0x000000615c30: 0x0000002a 0x00000000 (Int value 42) 0x000000615c2c: 0x00000001 (Weak reference counter + offset 1) 0x000000615c28: 0x00000002 (Shared reference counter) C Blk 0x000000615c20: 0x00401970 0x00000000 (Pointer to allocator, deleter, ...) ---------------------
Insides
If you create the shared pointer not with std::make_shared but with an explicit new the object and the control block are stored at two different memory locations. The control block contains then an additional pointer to the object.
When the shared/strong reference counter reaches zero the object gets destroyed. Afterwards only if the weak reference counter is/reaches zero the control block gets destroyed.