set.clear()在删除元素之前调用包含元素的析构函数

时间:2013-12-24 05:12:19

标签: c++ stl g++ libstdc++

在下面的代码中,我希望断言能够通过,但事实并非如此。

这与documented behavior of unique_ptr::reset不同,我觉得这很令人惊讶。

我做错了什么或是错误吗?这是一个问题,因为如果删除相同的元素增益,则会调用析构函数两次。

#include <set>
#include <memory>

struct F
    : std::enable_shared_from_this<F>
{
    static int destructor_count;
    static std::set<std::shared_ptr<F>> container;

    F() {}

    ~F() {
        assert(container.size() == 0);
        container.clear(); // This will delete the same pointer twice.
        destructor_count--;
    }
};

int F::destructor_count = 0;
std::set<std::shared_ptr<F>> F::container;

int main()
{
    F::container.insert(std::shared_ptr<F>(new F));
    F::container.clear();
    return 0;
}

编译器信息:

的libstdc ++ 6-4.6-dev的

g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

3 个答案:

答案 0 :(得分:2)

有一个原因unique_ptr被明确记录为以这种方式行事:它比通常用于标准库的其他部分的保证更强。如果同一规则适用于所有类型,则无需专门针对unique_ptr声明。

您的代码递归调用F::container.clear(),而另一个F::container.clear()调用正在运行。这不能保证有效:

  

17.6.5.8可重入[reentrancy]
   除非在本标准中明确指定,否则它是实现定义的,可以递归地重新输入标准C ++库中的函数。

现在不幸的是libstdc ++无法记录哪些函数可以递归重新输入,所以假设没有一个函数可以更安全,所以你的断言是不正确的。

答案 1 :(得分:1)

在清空容器时调用F的析构函数,在此期间容器不为空,因此断言失败。

答案 2 :(得分:1)

我不确定为什么你会认为断言是真的。您不应该知道容器如何容纳和破坏内存的细节。你不应该对std::set::clear()期间发生的事情做出假设,只相信在完成所有析构函数之后,std::set::size()将返回0。

在这篇旧Dobbs博士文章STL's Red-Black Trees中,描述了std::set背后的支持数据结构。在整个树为空之前,节点将被删除(并且它们的内容被破坏),但是在std::set::clear()结束时将保证将调用所有析构函数并且std::set::size()将返回0。

注意:std::set的其他实现可能使用不同的后备数据结构,红黑树只是一种可能的实现。