FIFO类型的容器 - 哪种STL容器最合适,为什么?

时间:2011-11-04 20:25:37

标签: c++ stl containers

我最近负责实现一个缓冲区,该缓冲区将被日志记录类用作临时存储。日志记录类本身是一个单例,并使用了观察者监听器模式。您可以使用此类记录数千条消息。

现在问题在于:

我们有一个跟踪记录选项,用于除垢目的。启用此选项后,消息/秒的计数将以指数方式增加。在发布代码中,跟踪日志记录被禁用,但是一个缓冲区可以存储固定数量的消息,例如10000被转储到日志中 IF 发生错误,以便开发人员可以识别问题的根源。

如果缓冲区已满,则删除最旧的消息以释放最新消息的空间。

void Log::storeToBuffer(const LogType_E & type_in, const LogDomain_E & domain_in,const int & id_in, const char * msg_in)
{
    if(this->myEnableTraceBuffer)
    {
        if(static_cast<std::list<Message> * >(this->myRingBuffer)->size() < this->myRingBufferMaxSize)
        {
            static_cast<std::list<Message> * >(this->myRingBuffer)->push_back(Message(type_in, domain_in, id_in, msg_in));
        }
        else
        {
            //buffer full so remove oldest element and add new
            if(static_cast<std::list<Message> * >(this->myRingBuffer)->size() > 0) static_cast<std::list<Message> * >(this->myRingBuffer)->pop_front();
            static_cast<std::list<Message> * >(this->myRingBuffer)->push_back(Message(type_in, domain_in, id_in, msg_in));
        }
    }
}

我使用std :: list实现了这一点,非常简单地使用push_back / pop_front来利用常量删除/插入执行时间。 (不要求空洞投射,而不是我的决定)。

但由于缓冲区大小是固定的,并且在对象的生命周期内不太可能改变,因此具有显式索引操作的向量可能更合适吗?例如,可以有两个indeces,开始/当前在位置0处开始。当向量已满并且我们添加一些内容时,开始移动到位置1并且当前到位置0,因此在打印结果时我们有正确的顺序。

也许另一个STL容器更适合这种东西?

感谢您耐心阅读这长篇文章。我在这里回答任何问题。

4 个答案:

答案 0 :(得分:3)

std::deque可以在开头或结尾有效地插入或删除,使其成为缓冲区的优秀结构。它可以用作std::queue的模板类型,它非常适合您的应用程序 - 每次执行push时只需检查队列的大小,如果大小是pop,则{{1}}超过最大值。

答案 1 :(得分:2)

有没有听说过std::deque? :)
它允许在两侧进行摊销的恒定时间随机访问和恒定时间推/弹操作。因此,它可以很容易地用作FIFO队列。

也就是说,因为它可以很容易地使用,所以有一个容器适配器std::queue,虽然它不提供随机访问和迭代器接口。

答案 2 :(得分:2)

虽然std :: list的push_back / pop_front是常量时间,但它们将使用堆分配,这可能是你的瓶颈。

使用std :: vector描述的方法将消除堆分配,因此可能会更快。 如你所描述的那样使用std :: vector是一个循环缓冲区,如果你可以使用boost,那么这里有一个符合stl标准的:http://www.boost.org/doc/libs/1_47_0/libs/circular_buffer/doc/circular_buffer.html

答案 3 :(得分:2)

您正在寻找的是双端队列,其中可以从前/后删除元素,并且C ++标准库的版本为std::deque。它的功能类似于std::vector(例如,通过索引访问,但要注意,不能保证连续存储),但支持在正面和背面快速插入和删除。我认为在内部实际上它实现为像环形缓冲区一样使用的普通数组。

我认为这里唯一的问题是它没有提供reserve方法。因此,您不能从一开始就说它应该为您的潜在10000元素腾出空间,并且必须考虑一些重新分配。但这些应该分摊,就像矢量一样。仍然优于std::list 10000或更多的小分配。一旦它的大小为10000个元素,就不应再有任何分配,而std::list将不断释放和分配新对象。

实际上你也可以使用std::queue,它默认使用std::deque作为其底层容器类型,并且只提供FIFO队列所需的少数功能。但在这种情况下,您将无法访问单个元素或迭代所有元素,这可能是输出逻辑所需的。