Search code examples
c++shared-ptrstdmap

I put a shared_ptr into a map, but why is the object destructed before the program ends?


I create a shared_ptr for my test object, and put it in a std::map, but its destructor is called before the program ends, and I don't know why.

Here is my test code:

class Test
{
public:
    Test()
    {
        std::cout << "in constructor" << std::endl;
    }

    ~Test()
    {
        std::cout << "in ~constructor" << std::endl;
    }

    Test(Test&&)      = delete;
    Test(const Test&) = delete;
};
    
std::map<std::string, std::shared_ptr<Test>> datas;

int main(int argc, char* argv[])
{
    if (true)
    {
        auto temp = datas["key"];
        if (!temp)
        {
            std::cout << "create new one" << std::endl;
            temp = std::make_shared<Test>();
            datas.insert(std::make_pair("key", temp));
        }
        else
        {
            std::cout << "already exists,pass" << std::endl;
        }

        std::cout << "temp use count:" << temp.use_count() << std::endl;
    }

    auto other = datas["key"];

    std::cout << "other use count:" << other.use_count() << std::endl;

    while (true)
    {
        usleep(100);
    }

    std::cout << "program end" << std::endl;

    return 0;
}

When I run the program, it waits at line while (true), but the output is:

create new one
in constructor
temp use count:1
in ~constructor
other use count:0

Before the program ends, I think my object should still be in memory, because the map holds a reference.

Another question: when I get other from the map, why is the use count 0? How to get the object back the right way?

BTW, if I comment out if(true){}, the destructor is not called, but other's use count is still 0.


Solution

  • operator [] of std::map will insert element if not present in the map. std::map::insert() will fail to insert anything if the element is already in the map. Thus, your insert() call fails. You can verify it by checking the return value:

    auto [iterator, wasInserted] = datas.insert(std::make_pair("key", temp));
    std::cout << "insert call was " << (wasInserted ? "successful" : "unsuccessful") << '\n';
    

    To update or create element if it doesn't exist, you can use operator[] again:

        if (!temp)
        {
            std::cout << "create new one" << std::endl;
            temp = std::make_shared<Test>();
            datas["key"] = temp;
        }