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

set.clear() calls destructors of contained elements before removing the element

本文关键字:元素 调用 包含 析构函数 删除 set clear      更新时间:2023-10-16

在下面的代码中,我希望断言能够通过,但它没有。

这与记录的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.
明确

记录unique_ptr这样做是有原因的:它比通常对标准库的其余部分有更强的保证。如果相同的规则适用于所有类型,则无需专门针对unique_ptr进行说明。

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

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

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

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

我不确定你为什么会期望这个断言是正确的。您不需要知道容器如何保存和销毁内存的详细信息。你不应该对std::set::clear()期间发生的事情做出假设,只相信在它完成后,所有的析构函数都会被调用,std::set::size()将返回 0。

在这篇多布斯博士的旧文章《STL的红黑树》中,描述了std::set背后的支持数据结构。在整个树为空之前,节点将被删除(并且它们的内容被破坏),但请放心,在std::set::clear()结束时,所有析构函数都将被调用,std::set::size()将返回 0。

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