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

deleting shared_ptrs from unordered_map when deleting shared_ptr from vector ( simulatiously)

让我们考虑这段代码:

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

}

简而言之,我使用向量来存储 shared_pointers 来存储 World 中的所有动物。 我使用无序地图通过坐标即时访问动物(int 值将基于生物体的二维位置,我可能会将其更改为 std::pair 但跳过它,现在不重要) .

我想在 std::vector 中为 shared_ptrs 创建一个自定义删除器,它也将删除指向 std::map 中相同对象的 shared_ptr(因为如果有些东西不在我的 animals_vector 里面 它不能在我的 organisms_map).

里面

我问的是如何为 std::shared_ptr.

编写这样一个指定的析构函数

你的方法行不通,因为你的自定义删除器不会执行,直到给定 Organism 的最后一个 shared_ptr 消失,并且因为你保留了每个 Organism 在两个不同的数据结构中,在您从 animals_vector.

中删除一个后,您的 animals_map 中总会有第二个 shared_ptr

作为一个可行的替代方案,我建议编写您自己的 remove_animal(std::shared_ptr) 方法来从两个数据结构中删除条目,并让您的代码改为调用该方法。 (使这两个数据结构 private 可以帮助确保所有调用代码都使用您的方法而不是尝试直接访问数据结构,因此不会无意中违反您的约束)

我不认为你的问题需要自定义删除器,我认为你主要 运行 遇到问题是因为你做了双重簿记。 您有一张地图和一个包含相同信息的矢量,即您世界中的一组动物。我建议你只保留地图。

您需要返回的是一种迭代地图中所有动物的简单方法,为此您可以使用自定义迭代器。这个例子向您展示了如何实现这样的语法:

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

完整源代码: (PS。我仍在仔细检查自定义迭代器的完全正确性, 我不经常写它们,但对于这个例子,它有效)

// 

#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;
    }
}