weak_ptr observes a shared_ptr's object without owning it. It doesn't affect the reference count. When all shared_ptrs die, the object is deleted even if weak_ptrs still exist. Use lock() to get a shared_ptr from a weak_ptr.
If the object still exists, you get a valid shared_ptr. If it's been deleted, you get nullptr. Always check before using. weak_ptr breaks circular references. If object A has a shared_ptr to B and B has one to A, neither can delete.
Make one direction weak_ptr and the cycle breaks.