【问题标题】:How would I use for_each to delete every value in an STL map?我将如何使用 for_each 删除 STL 映射中的每个值?
【发布时间】:2010-03-17 15:26:19
【问题描述】:

假设我有一个 STL 映射,其中的值是指针,我想将它们全部删除。我将如何表示以下代码,但使用 std::for_each?我很高兴有使用 Boost 的解决方案。

for( stdext::hash_map<int, Foo *>::iterator ir = myMap.begin();
     ir != myMap.end();
     ++ir )
{
  delete ir->second; // delete all the (Foo *) values.
}

(我找到了 Boost 的 checked_delete,但我不确定如何将其应用于迭代器所代表的 pair&lt;int, Foo *&gt;)。

(另外,出于这个问题的目的,忽略在 STL 容器中存储需要删除的原始指针不是很明智的事实)。

注意:我随后在下面找到并列出了一个单行答案......但代码非常糟糕,所以我接受了 GMan 的更理智的答案。

【问题讨论】:

  • 在迭代器上很好地使用预增量操作!

标签: c++ algorithm boost stl


【解决方案1】:

你必须创建一个函数对象:

struct second_deleter
{
    template <typename T>
    void operator()(const T& pX) const
    {
        delete pX.second;
    }
};

std::for_each(myMap.begin(), myMap.end(), second_deleter());

如果你使用 boost,你也可以使用 lambda 库:

namespace bl = boost::lambda;
std::for_each(myMap.begin(), myMap.end(), second_deleter(),
                bl::bind(bl::delete_ptr(), 
                bl::bind(std::select2nd<myMap::value_type>(), _1));

但您可以尝试使用自动执行此操作的 pointer containers 库。

请注意,您使用的不是地图,而是hash_map。我建议你切换到 boost 的unordered_map,它更流行。不过好像没有ptr_unordered_map

为了安全起见,你应该把这东西包起来。例如:

template <typename T, typename Deleter>
struct wrapped_container
{
    typedef T container_type;
    typedef Deleter deleter_type;

    wrapped_container(const T& pContainer) :
    container(pContainer)
    {}

    ~wrapped_container(void)
    {
        std::for_each(container.begin(), container.end(), deleter_type());
    }

    T container;
};

并像这样使用它:

typedef wrapped_container<
            boost::unordered_map<int, Foo*>, second_deleter> my_container;

my_container.container./* ... */

这确保无论如何,您的容器都将使用删除器进行迭代。 (例如,例外情况。)

比较:

std::vector<int*> v;
v.push_back(new int);

throw "leaks!"; // nothing in vector is deleted

wrapped_container<std::vector<int*> > v;
v.container.push_back(new int);

throw "no leaks!"; // wrapped_container destructs, deletes elements

【讨论】:

  • 我想可能是这样的。我曾希望无需定义自己的类型就可以做到这一点。我想知道是否可以使用 boost::bind 或 select2nd 之类的?
  • 如果地图决定重新平衡自身并因此开始复制已删除的指针,这不会在技术上调用 UB 吗?也许他们也无效? - 只是注意到它是一个 hash_map 而不是一个地图,所以它不会重新平衡
  • @jk:这并没有从地图中删除任何东西,只是删除了它指向的对象。
  • @jk:为什么地图会随机将地图中的指针设置为空?那将是一张非常破损的地图。
  • OP 没有说这是在容器本身被销毁之前。如果发生其他任何事情导致容器复制其任何内部内容,那么它将复制一个已删除的指针,这在技术上是 UB iirc。将指针归零将使它们可以安全地再次复制。
【解决方案2】:

您是否尝试过使用 BOOST_FOREACH ?这应该允许您在不创建自己的函子的情况下连续执行此操作。

我还没有测试过以下代码,但它应该看起来像这样(如果不完全一样的话):

typedef stdext::hash_map<int, Foo *> MyMapType; //see comment.
BOOST_FOREACH( MyMapType::value_type& p, myMap )
{
    delete p.second;
}

由于 typedef,这不止 1 行 :)

【讨论】:

  • 一般都不错。不幸的是,它是一个宏,不能处理&lt;&gt;之间的逗号
  • 啊,是的,需要一个 typedef,抱歉,不再是一行。
【解决方案3】:

好的,我发现了如何在一行中做到这一点......但我认为我不会在实际代码中实际执行以下操作!

std::for_each( mayMap.begin()
             , myMap.end()
             , boost::bind( &boost::checked_delete<Foo>
                          , boost::bind( &stdext::hash_map<int, Foo *>::value_type::second, _1 ) ) );

不过,我会接受 GMan 的回答,因为我喜欢他关于包装容器的想法,而我的回答,尽管是按要求写的一行,但很讨厌。

【讨论】:

    【解决方案4】:

    如果可能,您应该在地图中使用智能指针。

    这里使用智能指针消除了重构和调试成员删除的需要。少了一个内存管理来担心前进。每当我使用 new/delete 时,我都会认真考虑是否需要这样做。个人的“代码味道”(Martin Fowler),如果你愿意的话。

    当然,如果您的旧代码返回地图,那么 for_each 方法可能是您的最佳选择 - 但如果您参与创建地图,我建议您使用智能指针。

    【讨论】:

    • 在某个时候应该是可能的。我目前正在重构一些相当旧的代码。事实上,我只是好奇我是否可以在一行代码中做到这一点。但你是绝对正确的,最终我想到达那里......但这是一百万行 15 岁代码的一部分。
    • 如果你需要动态分配元素,但不适用共享所有权的概念,你可以看看boost::ptr_map
    猜你喜欢
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多