std::queue 和 std::deque 清理
std::queue and std::deque cleanup
假设我们有一种情况需要 FIFO 数据结构。例如,按照事件进来的顺序消费一些事件。
此外,我们需要不时地清除整个队列。
std::queue
似乎非常适合这样做,但不幸的是它缺少清除容器的功能。
所以在这一点上,我们有两个选择:
std::queue
- 我们向 STL 库询问了我们需要什么。当然,STL 库会给我们更多:它会给我们一个伪装成
std::queue
的 std::deque
- 我们只得到了我们需要的一部分,即 pop front 和 push back 但没有清除
- 我们将不得不以某种方式“模拟”清除,而没有循环和弹出的幼稚方式
std::deque
- 我们向 STL 库询问了我们需要什么
- 我们得到了我们要求的东西,但我们得到的太多了:我们还得到了前推和后推
总的来说,我们收到的要么太少要么太多,从来都不是我们真正想要的。
这是让我吃惊的事情,当我试图提供与 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
是否会使 _pending
处于 valid but unspecified
状态。请参阅那里的 string example。
我很惊讶规范对这个主题如此含糊。我找错地方了吗?
谢谢大家!
更新
似乎分配和清除之间存在不可忽视的区别。在内部,queue 和 deque 几乎是一样的(一个正在使用另一个)
要清空队列,也可以简单写
_pending = {};
注意:第一种方法可能无效,不应依赖,因为 moved-form 对象处于有效但未指定的状态。
we got back what we asked for, but we've got too much: we also got push front and pop back
您得到了您想要的,dequeue
是一种允许在任一端点进行高效插入和删除的数据结构。它可能不是适合您的数据结构,但选择它是您的错。
we will have to "emulate" clear somehow, without the naive way of looping and popping
根据记录,弹出在性能方面非常便宜,它只是减少了一个数字。 while 循环中的弹出转换为将整数递减直到 0,除非您有 很多 个数字,否则速度非常快。事实上,它可能比分配内存快得多,这将我们带到:
清除这些集合的 STL 方法 类 是用一个空集合交换它们(这是您自己想出来的)或者直接 re-allocate 它们就位(苹果的回答)。这两者都将(可能,标准对这一点含糊不清)分配内存,这是一个非常昂贵的线性操作。
您已准备就绪,但我建议您进行概要分析,看看哪种方式对您来说真的很重要。就个人而言,我只是在一个循环中弹出队列,它会在下次我需要推送更多时将分配的内存留在原地,因此它可以节省潜在的多次分配和 re-allocations (与重置队列相比),具体取决于关于您拥有的数据量。
std::move
的用法
std::move
不应以这种方式使用。您应该只使用 std::move
,好吧,当您不再使用对象时,将对象移动 到程序中的其他地方。正如您所说,它随后处于 有效但未指定的状态 :
- 有效 因为它完全可以安全销毁;
- 未指定 因为您不应再访问该对象。
std::queue
对比 std::deque
如果您只打算使用 FIFO 功能,我建议您使用 std::queue
。它确实清楚地表明您将 仅 将 std::deque
用作 FIFO 数据结构——清楚是 std::queue
存在的唯一原因.
清除 std::queue
您可以将它分配给一个空的 std::queue
,或者像您所做的那样,将它换成一个空的。
// ...
struct consumer
{
std::queue<message> _pending;
void clearQueue()
{
_pending = {};
}
};
根据DevSolar的评论编辑
假设我们有一种情况需要 FIFO 数据结构。例如,按照事件进来的顺序消费一些事件。
此外,我们需要不时地清除整个队列。
std::queue
似乎非常适合这样做,但不幸的是它缺少清除容器的功能。
所以在这一点上,我们有两个选择:
std::queue
- 我们向 STL 库询问了我们需要什么。当然,STL 库会给我们更多:它会给我们一个伪装成
std::queue
的 - 我们只得到了我们需要的一部分,即 pop front 和 push back 但没有清除
- 我们将不得不以某种方式“模拟”清除,而没有循环和弹出的幼稚方式
std::deque
std::deque
- 我们向 STL 库询问了我们需要什么
- 我们得到了我们要求的东西,但我们得到的太多了:我们还得到了前推和后推
总的来说,我们收到的要么太少要么太多,从来都不是我们真正想要的。
这是让我吃惊的事情,当我试图提供与 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
是否会使 _pending
处于 valid but unspecified
状态。请参阅那里的 string example。
我很惊讶规范对这个主题如此含糊。我找错地方了吗?
谢谢大家!
更新
似乎分配和清除之间存在不可忽视的区别。在内部,queue 和 deque 几乎是一样的(一个正在使用另一个)
要清空队列,也可以简单写
_pending = {};
注意:第一种方法可能无效,不应依赖,因为 moved-form 对象处于有效但未指定的状态。
we got back what we asked for, but we've got too much: we also got push front and pop back
您得到了您想要的,dequeue
是一种允许在任一端点进行高效插入和删除的数据结构。它可能不是适合您的数据结构,但选择它是您的错。
we will have to "emulate" clear somehow, without the naive way of looping and popping
根据记录,弹出在性能方面非常便宜,它只是减少了一个数字。 while 循环中的弹出转换为将整数递减直到 0,除非您有 很多 个数字,否则速度非常快。事实上,它可能比分配内存快得多,这将我们带到:
清除这些集合的 STL 方法 类 是用一个空集合交换它们(这是您自己想出来的)或者直接 re-allocate 它们就位(苹果的回答)。这两者都将(可能,标准对这一点含糊不清)分配内存,这是一个非常昂贵的线性操作。
您已准备就绪,但我建议您进行概要分析,看看哪种方式对您来说真的很重要。就个人而言,我只是在一个循环中弹出队列,它会在下次我需要推送更多时将分配的内存留在原地,因此它可以节省潜在的多次分配和 re-allocations (与重置队列相比),具体取决于关于您拥有的数据量。
std::move
的用法
std::move
不应以这种方式使用。您应该只使用 std::move
,好吧,当您不再使用对象时,将对象移动 到程序中的其他地方。正如您所说,它随后处于 有效但未指定的状态 :
- 有效 因为它完全可以安全销毁;
- 未指定 因为您不应再访问该对象。
std::queue
对比 std::deque
如果您只打算使用 FIFO 功能,我建议您使用 std::queue
。它确实清楚地表明您将 仅 将 std::deque
用作 FIFO 数据结构——清楚是 std::queue
存在的唯一原因.
清除 std::queue
您可以将它分配给一个空的 std::queue
,或者像您所做的那样,将它换成一个空的。
// ...
struct consumer
{
std::queue<message> _pending;
void clearQueue()
{
_pending = {};
}
};
根据DevSolar的评论编辑