Уничтоженный unique_ptr уничтожит ваш объект.
«Уничтожит» — это значит, вызовет деструктор и сделает память доступной менеджеру памяти. Теперь другие указатели, которые смотрят на этот объект, будут «висячими», и при их разыменовании программа может сделать ЧТО УГОДНО.
А теперь посмотрим, что именно ей угодно. Ваша функция foo() невиртуальная и не имеет дела с полями, ей нужен только this (указатель на объект), который даже не разыменовывается. Она в принципе не может вылететь, даже если десять раз занулить этот объект!
То, что вы хотите сделать — это семантика std::shared_ptr/weak_ptr.
std::shared_ptr<ttt> sh1 = std::make_shared<ttt>();
std::weak_ptr<ttt> we = sh1;
{
std::shared_ptr<ttt> sh2 = sh1;
sh1 = nullptr;
}
std::cout << we.expired(); // должно быть true
UPD. Вот для таких, как ты, и ввели в C++14 функцию std::make_unique, по аналогии с std::make_shared. Просто чтобы было меньше таких ошибок.