std::queue 和 std::d eque 清理

std::queue and std::deque cleanup

提问人:shiretu 提问时间:3/31/2022 最后编辑:shiretu 更新时间:4/1/2022 访问量:1114

问:

假设我们有一个情况,我们需要FIFO数据结构。例如,按事件出现的顺序使用某些事件。

此外,我们需要不时清除整个队列。

std::queue似乎非常适合这样做,但不幸的是,它缺乏清理容器的功能。

因此,在这一点上,我们有 2 种选择:

std::queue

  • 我们问 STL 库我们需要什么。诚然,STL 库会给我们更多:它会给我们一个伪装成一个std::dequestd::queue
  • 我们只从我们需要的东西中取回了一部分,即弹出前和推回,但没有明确
  • 我们将不得不以某种方式“模仿”清除,而不是循环和弹出的幼稚方式

std::deque

  • 我们问 STL 库我们需要什么
  • 我们得到了我们要求的东西,但我们得到了太多:我们也得到了前推和后推

总的来说,我们得到的要么太少,要么太多,从来都不是我们真正想要的。

这是让我感到惊讶的事情,当我试图提供明确的功能来使用我的对象的成员 var 时std::queue

struct message
{
};
struct consumer
{
    std::queue<message> _pending;

    void clear_ver_1()
    {
       auto will_be_deleted_when_out_of_scope = std::move(_pending);
    }

    void clear_ver_2()
    {
        std::queue<message> will_be_deleted_when_out_of_scope;
        _pending.swap(will_be_deleted_when_out_of_scope);
    }
};

我已经阅读了规格,我不能确定是否会保持状态。请参阅此处的字符串示例clear_ver_1_pendingvalid but unspecified

我很惊讶这个话题的规格如此模糊。我是不是找对地方了?

谢谢大家!

更新

分配和清算之间似乎存在不可忽视的区别。在内部,queue 和 deque 几乎相同(一个使用另一个)

enter image description here

C++ STL 标准

评论

1赞 wohlstad 3/31/2022
看起来是要走的路:stackoverflow.com/questions/709146/......clear_ver_2
1赞 PaulMcKenzie 3/31/2022
令我惊讶的是,规范对这个主题如此模糊——“除非另有说明,否则所有已移出的标准库对象都处于”有效但未指定的状态”
0赞 shiretu 3/31/2022
@PaulMcKenzie:说得好。它还以 继续。所以这意味着 std::queue::front 可以安全调用吗?它没有预编词 (en.cppreference.com/w/cpp/container/queue/frontmeaning the object's class invariants hold (so functions without preconditions, such as the assignment operator, can be safely used on the object after it was moved from))

答:

0赞 apple apple 3/31/2022 #1

要清除队列,您也可以简单地编写

_pending = {};

注意:第一种方法可能不起作用,不应依赖,因为移动表单对象处于有效但未指定的状态。

1赞 Blindy 3/31/2022 #2

我们得到了我们要求的东西,但我们得到了太多:我们也得到了前推和后推

你得到了你所要求的,a 是一种数据结构,允许在任一端点进行有效的插入和删除。它可能不是适合您的数据结构,但这是您选择它的错。dequeue

我们将不得不以某种方式“模仿”清除,而不是循环和弹出的幼稚方式

根据记录,爆裂在性能方面非常便宜,它只是递减了一个数字。在 while 循环中弹出一个整数转换为将整数递减到 0,除非你有很多数字,否则这是非常快的。事实上,它可能比分配内存快得多,这给我们带来了:

清除这些集合类的 STL 方法是将它们与空集合交换(这是你自己想出的),或者直接将它们重新分配到位(Apple 的答案)。这两种方法(可能,标准在这一点上是模糊的)都会分配内存,这是一个非常昂贵的线性操作。

你有所有的部分来做这件事,尽管我建议分析一下,看看哪种方式更快,如果它真的对你很重要。就我个人而言,我只是将队列弹出一个循环,它会将分配的内存保留在原位,以备下次我需要推送更多时使用,因此它可以节省潜在的多次分配和重新分配(与重置队列相比),具体取决于您拥有的数据量。

评论

0赞 shiretu 3/31/2022
关于向 STL 请求 std::d eque 的观点是,如果我也想要 clear(),我必须要求 deque,这是一种强制选项。所以作为用户并不是我的错,因为没有好的选择。从概念上讲,我要求流行前推、后推和清除,但提供了一个替代方案,可以做额外的事情。
0赞 Blindy 3/31/2022
从概念上讲,你要求流行前,后推和清除,没有得到这样的集合,然后选择其他东西。,,不管怎样,当你在STL中没有发现任何符合你要求的东西时,你做出了这个替代选择。dequeuevector
0赞 shiretu 4/1/2022
事实上。事情就是这样发生的。但也许我们可以不那么迂腐一点,理解这里的核心问题:当你需要一个 5 美元的工具来做某事时,你唯一的选择是那个工具,但是一个专业版本,可以做额外的事情并花费 150 美元,你会怎么做!?您可以自己购买或制作。如果你真的买了它,这并不是一个全心全意的决定。是的,我做出了这个决定;我支持它。但更像是被胁迫。
0赞 shiretu 4/1/2022
请参阅更新后的帖子。看来爆裂很便宜,你是对的
5赞 Debaug 3/31/2022 #3

的用法std::move

std::move并不意味着以这种方式使用。你应该只使用 ,好吧,当你不再使用它时,将对象移动到程序中的其他位置。正如您所说,它随后处于有效但未指定的状态std::move

  • 有效,因为它完全可以安全地销毁;
  • 未指定,因为您不应再访问该对象。

std::queuestd::deque

如果您只打算使用 FIFO 功能,我建议使用 .它清楚地表明,你只会用作 FIFO 数据结构——清晰度是首先存在的唯一原因。std::queuestd::dequestd::queue

清除std::queue

您可以将其分配给空的,或者像您所做的那样,将其换成空的。std::queue

// ...

struct consumer
{
    std::queue<message> _pending;

    void clearQueue()
    {
        _pending = {};
    }
};

根据DevSolar的评论进行编辑

评论

1赞 DevSolar 3/31/2022
像往常一样,如果你的解决方案看起来很复杂,那通常是因为你做错了。;-)我也不会为函数而烦恼。只是会做得很好,很容易被理解。clearQueue_pending = {}
1赞 shiretu 3/31/2022
@DevSolar:那么它引出了一个问题:为什么有带有 clear() 的容器和没有 clear() 的容器?队列有什么特别之处,以至于它没有?我认为使用FIFO时是常识性功能:能够清除它。
2赞 shiretu 3/31/2022
@DevSolar:我更困惑为什么deque有清晰。它也可以用 ' = {}' 重置。然而,它有明确的 clear()
1赞 DevSolar 3/31/2022
@shiretu:不是容器,就像不是一样。这些是容器适配器,它们围绕实际容器类型包装语义 API(队列或堆栈的语义 API)。的默认容器是 。如果您愿意,也可以声明一个。显然,这些适配器的设计者不同意你的观点,因为他们从事的是编辑业务。 (我正在努力思考为什么你会列出 FIFO 列表并丢失这些事件......std::queuestd::stackstd::queuestd::dequestd::queue< message, std::map >clear()clear()
1赞 DevSolar 3/31/2022
@balu:见上文。容器可以,容器适配器不能。clear()