简体   繁体   English

从向量中删除 shared_ptr 时从 unordered_map 中删除 shared_ptrs(模拟)

[英]deleting shared_ptrs from unordered_map when deleting shared_ptr from vector ( simulatiously)

Let's consider this piece of code:让我们考虑这段代码:

class Organism
{ //some code here..
}
class World
{    
    unordered_map<int, std::shared_ptr<Organism>> organims_map;
    vector <std::shared_ptr<Organism>> animals_vector;
    add_organisms { 
    //i want to create specified shared_ptr destructor here

}

Shortly speaking I use a vector to store shared_pointers to store all animals inside World.简而言之,我使用一个向量来存储 shared_pointers 来存储 World 中的所有动物。 I use an unordered map for instant access to animals by their coordinates ( int value will be based on the 2-dimensional position of the organism, I may change it to std::pair but just skip it, it's not important for now).我使用无序地图通过它们的坐标即时访问动物( int 值将基于生物体的二维位置,我可以将其更改为std::pair但只是跳过它,目前这并不重要)。

I want to have a custom deleter to shared_ptrs that is inside std::vector which will also delete shared_ptr that points to the same object in std::map ( Because if something is not inside my animals_vector IT MUST NOT BE INSIDE MY organisms_map ).我想在std::vector内为shared_ptrs设置一个自定义删除器,它也将删除指向std::map相同对象的shared_ptr (因为如果我的animals_vector内没有某些东西,它不得在我的organisms_map )。

I'm asking about how to write such a specified destructor for std::shared_ptr .我在问如何为std::shared_ptr编写这样一个指定的析构函数。

Your approach won't work, because your custom-deleter won't execute until the last shared_ptr to a given Organism has gone away, and since you are keeping each Organism in two different data structures, there will always be a second shared_ptr in your animals_map after you removed the one from the animals_vector .你的方法行不通,因为你的自定义删除器不会执行,直到给定Organism的最后一个shared_ptr消失,而且由于你将每个Organism保存在两个不同的数据结构中,所以你的文件中总会有第二个shared_ptranimals_vector删除一个animals_map之后。

As a workable alternative, I suggest writing your own remove_animal(std::shared_ptr) method that removes the entry from both data structures, and have your code call that method instead.作为一个可行的替代方案,我建议编写您自己的remove_animal(std::shared_ptr)方法,从两个数据结构中删除条目,并让您的代码调用该方法。 (Making both of the data structures private can help ensure that all of the calling code uses your methods rather than trying to access the data structures directly, and therefore that your constraints don't get unintentionally violated) (将两个数据结构设为private有助于确保所有调用代码都使用您的方法,而不是尝试直接访问数据结构,因此您的约束不会被无意违反)

I don't think your problem asks for a custom deleter, I think you mainly run into problems because you do double bookkeeping.我不认为您的问题需要自定义删除器,我认为您遇到的问题主要是因为您进行了双重簿记。 You have both a map and a vector containing the same information, a set of animals in your world.您有一张地图和一个包含相同信息的矢量,即您世界中的一组动物。 I would propose you only keep the map.我建议你只保留地图。

What you need back is an easy way to iterate over all the animals in your map, for that you can use a custom iterator.您需要返回的是一种迭代地图中所有动物的简单方法,为此您可以使用自定义迭代器。 And this example shows you how you can achieve a syntax like this :这个例子向你展示了如何实现这样的语法:

int main()
{
    world_t world;
    for (const auto& animal : world.animals())
    {
        std::cout << animal << std::endl;
    }
}

Full source code : (PS. I am still double-checking the full correctness of the custom iterator, I don't write them often, but for this example, it works)完整源代码:(PS。我仍在仔细检查自定义迭代器的完全正确性,我不经常编写它们,但对于这个例子,它有效)

// https://stackoverflow.com/questions/69243916/deleting-shared-ptrs-from-unordered-map-when-deleting-shared-ptr-from-vector-s

#include <algorithm>
#include <map>
#include <string>
#include <iostream>
#include <vector>

//=================================================================================================
// map_value_iterator.h

//-------------------------------------------------------------------------------------------------
// implementation of a custom iterator over std::map that returns the values

namespace impl
{
    template<typename key_t, typename value_t>
    struct const_map_value_iterator_t
    {
        using iterator_category = std::forward_iterator_tag;
        using value_type = value_t; 
        using difference_type = std::ptrdiff_t;
        using pointer = const value_t*; // <== not completely sure this is correct, todo check
        using reference = const value_t&;

        // the underlying map iterator type
        using underlying_iterator_t = typename std::map<key_t, value_t>::const_iterator;

        const_map_value_iterator_t() = default;

        explicit const_map_value_iterator_t(const underlying_iterator_t& it) :
            m_map_iterator{ it }
        {
        }
    
        const_map_value_iterator_t(const const_map_value_iterator_t& rhs) :
            m_map_iterator{ rhs.m_map_iterator }
        {
        }
    
        const_map_value_iterator_t& operator=(const const_map_value_iterator_t& rhs)
        {
            m_map_iterator = rhs.m_map_iterator;
        }

        reference operator*() const
        {
            return (m_map_iterator->second);
        }

        pointer operator->() const
        {
            return &(m_map_iterator->second);
        }

        const_map_value_iterator_t& operator++()
        {
            ++m_map_iterator;
            return *this;
        }

        const_map_value_iterator_t& operator++(int)
        {
            ++m_map_iterator;
            return *this;
        }

        const_map_value_iterator_t& operator--()
        {
            --m_map_iterator;
            return *this;
        }

        const_map_value_iterator_t& operator--(int)
        {
            --m_map_iterator;
            return *this;
        }

        bool operator==(const const_map_value_iterator_t& rhs)
        {
            return m_map_iterator == rhs.m_map_iterator;
        }

        bool operator!=(const const_map_value_iterator_t& rhs)
        {
            return m_map_iterator != rhs.m_map_iterator;
        }

    private:
        underlying_iterator_t m_map_iterator;
    };

} /* namespace impl */

//-------------------------------------------------------------------------------------------------
// struct to initialize iterator based on a map
// and to provide a nice grouping of begin/end
// so both can be returned by one getter.

template<typename key_t, typename value_t>
struct map_values_iterator_t
{
    explicit map_values_iterator_t(const std::map<key_t, value_t>& map) :
        m_begin{ map.begin() },
        m_end{ map.end() }
    {
    }

    const auto& begin() const
    {
        return m_begin;
    };

    const auto& end() const 
    {
        return m_end;
    };

private:
    impl::const_map_value_iterator_t<key_t, value_t> m_begin;
    impl::const_map_value_iterator_t<key_t, value_t> m_end;
};

//=================================================================================================
// main.cpp
// inline example

struct animal_t
{
    animal_t(std::string n, std::size_t v) :
        name{ n },
        vigor{ v }
    {
    }

    // Sort orde by increasing vigor
    static bool order_by_vigor(const animal_t* lhs, const animal_t* rhs)
    {
        return lhs->vigor > rhs->vigor;
    }

    std::string name;
    std::size_t vigor;
};


class world_t 
{
public:

    // return the iterator over values in the map
    auto animals() 
    {
        return map_values_iterator_t{ m_map };
    }

    auto animals(bool (*sort_fn)(const animal_t* lhs, const animal_t* rhs))
    {
        // if your collection of animals is likely to change
        // then returning a sorted copy is probably safer
        // (remove the pointer)
        // I'd rather have used references, but they can't be sorted
        std::vector<const animal_t*> sorted_animals;

        for (const auto& animal : animals())
        {
            sorted_animals.push_back(&animal);
        }

        std::sort(sorted_animals.begin(), sorted_animals.end(), sort_fn);
        return sorted_animals;
    }

private:
    std::map<int, animal_t> m_map{ {1,{"bear",9}}, {3,{"cat",2}}, {5,{"dog",4}} };
};

//-------------------------------------------------------------------------------------------------

int main()
{
    world_t world;

    for (const auto& animal : world.animals(animal_t::order_by_vigor))
    {
        std::cout << "animal '" << animal->name << "' has vigor '" << animal->vigor << "'" << std::endl;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM