I was reading about thread safety of std::shared_ptr and about the atomic operations overloads it provides and was wondering regarding a specific use case of it in a class.
From my understanding of the thread safety that is promised from shared_ptr, it is safe to have a get method like so:
class MyClass 
{
std::shared_ptr<int> _obj;
public:
    void start() 
    {
        std::lock_guard<std::mutex> lock(_mtx);
        _obj = std::make_shared<int>(1);
    }
    void stop()
    {
          std::lock_guard<std::mutex> lock(_mtx);
        _obj.reset();
    }
    std::shared_ptr<int> get_obj() const
    {
        return _obj; //Safe (?)
    }
};
The getter should be safe since the object will either be initializes or empty at any point from any thread.
But what if I want to throw an exception if the object is empty, I need to check it before returning it, do I have to put a lock there now (since stop() might be called between the if and the return)? Or is it possible to use the locking mechanism of the shared pointer and not use a lock in this method:
   std::shared_ptr<int> get_obj() const
    {
        auto tmp = _obj;
        if(!tmp) throw std::exception();
        return tmp;
    }