XU Weijiang
XU Weijiang

Reputation: 78

Crash in destructor when using std::enable_shared_from_this

I am trying to write a boost-signal like class (as practice to learn c++). And I found when I use enable_shared_from_this, I always got crash in destructor. Here is the code (I am using vs2012 update 2):

Any comment on this? This issue should be caused shared_from_this(), because when I uncomment the line (auto this_ = shared_from_this();) in signal_connection::disconnect, everything works fine.

I know it must at least has one valid shared_ptr before calling shared_from_this(). My code should satisfy this.

Code:

    #include <memory>
    #include <map>

    class slot_manager;

    class signal_connection: public std::enable_shared_from_this<signal_connection>
    {
    public:
        signal_connection(slot_manager* manager)
            :manager_(manager)
        {}
    public:
        void disconnect() ;
    private:
        slot_manager* manager_;
    };

    class slot_manager
    {
    public:
        typedef std::shared_ptr<signal_connection> connection_type;
        typedef std::map<connection_type, int> map_type;
        typedef map_type::value_type map_value_type;
    public:
        void connect(int slot)
        {
            std::shared_ptr<signal_connection> c(new signal_connection(this));
            slots_.insert(map_value_type(c, slot));
        }
        ~slot_manager()
        {
            auto iter = slots_.begin();
            map_type::iterator iter2 = slots_.end();
            while (iter != slots_.end())
            {
                iter2 = iter++;
                iter2->first->disconnect();
            }
        }
        void disconnect(std::shared_ptr<signal_connection> connection)
        {
            auto c = slots_.find(connection);
            if (c != slots_.end())
            {
                slots_.erase(c);
            }
        }
    protected:
        map_type slots_;
    };

    void signal_connection::disconnect() 
    {
        if (manager_ != nullptr)
        {
            //auto this_ = shared_from_this();
            manager_->disconnect(shared_from_this());
            manager_ = nullptr;
        }
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        slot_manager x;
        x.connect(1);
    return 0;
    }

Upvotes: 1

Views: 1409

Answers (1)

ltjax
ltjax

Reputation: 15997

The problem is that the signal_connection is detroyed in disconnect() before you set its manager to nullptr, which then causes an access violation, i.e. by the call to the manager's disconnect. This is because the manager holds the only shared_ptr to the connection, and you erase() that. The object doesn't keep itself alive. Unless, of course, you uncomment that line. this_ just lives long enough to circumvent the problem.

Upvotes: 7

Related Questions