Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/powerbi/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 当我们需要存储“时,列表是否比向量更好?”;最后n个项目是什么;?_C++_C++11_Fifo_Buffering_Data Stream - Fatal编程技术网

C++ 当我们需要存储“时,列表是否比向量更好?”;最后n个项目是什么;?

C++ 当我们需要存储“时,列表是否比向量更好?”;最后n个项目是什么;?,c++,c++11,fifo,buffering,data-stream,C++,C++11,Fifo,Buffering,Data Stream,有很多问题建议我们应该始终使用向量,但在我看来,对于需要存储“最后n项”的场景,列表会更好 例如,假设我们需要存储看到的最后5项: 迭代0: 3,24,51,62,37, 然后在每次迭代中,删除索引0处的项,并在末尾添加新项: 迭代1: 24,51,62,37,8 迭代2: 51,62,37,8,12 在这个用例中,对于一个向量,复杂度将是O(n),因为我们必须复制n个项目,但是在一个列表中,它应该是O(1),因为我们总是在每个迭代中砍掉头部,并添加到尾部 我的理解正确吗?这是std::l

有很多问题建议我们应该始终使用向量,但在我看来,对于需要存储“最后n项”的场景,列表会更好

例如,假设我们需要存储看到的最后5项: 迭代0:

3,24,51,62,37,
然后在每次迭代中,删除索引0处的项,并在末尾添加新项:

迭代1:

24,51,62,37,8
迭代2:

51,62,37,8,12
在这个用例中,对于一个向量,复杂度将是O(n),因为我们必须复制n个项目,但是在一个列表中,它应该是O(1),因为我们总是在每个迭代中砍掉头部,并添加到尾部

我的理解正确吗?这是std::list的实际行为吗?

是一个更好的选择。或者,如果您对std::deque进行了基准测试,发现它的性能不适合您的特定用途,那么您可以在固定大小的数组中实现一个循环缓冲区,存储缓冲区开头的索引。在替换缓冲区中的元素时,您将覆盖起始索引处的元素,然后将起始索引设置为其以前的值加上缓冲区大小的一个模

列表遍历非常慢,因为列表元素可以分散在整个内存中,而向量移动实际上惊人地快,因为即使是一个大的内存块,内存在单个内存块上的移动也非常快


会议C++ 2015会议的谈话可能会引起你的兴趣。

< P>简要地说<代码> STD::向量< /代码>对于一个不改变大小的内存来说是更好的。在你的情况下,如果你把所有的数据向前移动或者在矢量中附加新的数据,那一定是浪费。正如@戴维所说的<代码>:STD: <代码>是一个不错的选择。因为您将
弹出头部
向后推
例如双向列表

从名单上看

与其他基本标准序列容器(数组、向量和 deque),列表在插入、提取和删除方面通常表现更好 在容器内的任何位置移动元素 迭代器已经获得,因此在算法中也是如此 它们会大量使用这些,比如排序算法

与其他列表相比,列表和转发列表的主要缺点 序列容器是指它们无法通过 他们的立场;例如,要访问列表中的第六个元素, 必须从已知位置(如开始或结束)进行迭代 结束)到该位置,该位置在 这些它们还消耗一些额外的内存来保持链接 与每个元素相关的信息(可能是重要的 小元素的大列表的系数)

关于

用于涉及频繁插入或删除图元的操作 在开始或结束以外的位置,DEQUE表现更差 和列表和引用相比,具有更少的一致性迭代器和引用 转发列表

因此,与数组相比,向量在交换中消耗更多的内存 能够以高效的方式管理存储和动态增长 对

与其他动态序列容器(DEQUE、LIST和 前向(U列表),向量非常有效地访问其元素 (就像数组一样)和相对高效的添加或删除 元素从其末端开始。对于涉及插入或删除的操作 如果在端部以外的位置移除图元,它们的性能会更差 而且迭代器和引用的一致性较差 比列表和转发列表


对。从末尾删除元素的std::vector的时间复杂度是线性的。对于您正在做的事情,std::deque可能是一个很好的选择,因为它在列表的开头和结尾提供了恒定的插入和删除时间,并且比std::list性能更好

资料来源:


如果您需要存储最后的
N
-元素,那么从逻辑上讲,您正在执行某种队列或循环缓冲区,并且是和队列的实现

您可以手动使用或实现简单的循环缓冲区:

template<int Capcity>
class cbuffer
{
public:
    cbuffer() : sz(0), p(0){}
    void push_back(int n)
    {
        buf[p++] = n;
        if (sz < Capcity)
            sz++;
        if (p >= Capcity)
            p = 0;
    }
    int size() const
    {
        return sz;
    }
    int operator[](int n) const
    {
        assert(n < sz);
        n = p - sz + n;
        if (n < 0)
            n += Capcity;
        return buf[n];
    }
    int buf[Capcity];
    int sz, p;
};
模板
cbuffer类
{
公众:
cbuffer():sz(0),p(0){}
无效推回(int n)
{
buf[p++]=n;
如果(深圳=Capcity)
p=0;
}
int size()常量
{
返回sz;
}
整数运算符[](整数n)常量
{
断言(n
对于5个int元素的循环缓冲区:

int main()
{
    cbuffer<5> buf;

    // insert random 100 numbers
    for (int i = 0; i < 100; ++i)
        buf.push_back(rand());

    // output to cout contents of the circular buffer
    for (int i = 0; i < buf.size(); ++i)
        cout << buf[i] << ' ';
}
intmain()
{
cbuffer-buf;
//随机插入100个数字
对于(int i=0;i<100;++i)
buf.向后推(rand());
//输出到循环缓冲区的cout内容
对于(int i=0;icout我认为即使使用std::deque,在某些情况下也会有复制项的开销,因为std::deque本质上是数组的映射,所以std::list是消除复制开销的一个好主意


为了提高std::list遍历的性能,您可以实现一个内存池,以便std::list将从主干及其空间位置分配内存进行缓存。

两者都不是。您的集合具有固定大小,并且
std::array
就足够了

您实现的数据结构称为环形缓冲区。要实现它,您需要创建一个数组并跟踪当前第一个元素的偏移量

当您添加一个元素将一个项目推出缓冲区时(即,当您删除第一个元素时),您将增加偏移量

要获取缓冲区中的元素,请添加索引和偏移量,并取其模和缓冲区长度。

如果可以使用Boost,请尝试:

// Create a circular buffer with a capacity for 5 integers. boost::circular_buffer<int> cb(5); // Insert elements into the buffer. cb.push_back(3); cb.push_back(24); cb.push_back(51); cb.push_back(62); cb.push_back(37); int a = cb[0]; // a == 3 int b = cb[1]; // b == 24 int c = cb[2]; // c == 51 // The buffer is full now, so pushing subsequent // elements will overwrite the front-most elements. cb.push_back(8); // overwrite 3 with 8 cb.push_back(12); // overwrite 24 with 12 // The buffer now contains 51, 62, 37, 8, 12. // Elements can be popped from either the front or the back. cb.pop_back(); // 12 is removed cb.pop_front(); // 51 is removed
#include <iterator>

template<typename Container>
class CircularBuffer
{
public:
    using iterator   = typename Container::iterator;
    using value_type = typename Container::value_type;
private:
    Container _container;
    iterator  _pos;
public:
    CircularBuffer() : _pos(std::begin(_container)) {}
public:
    value_type& operator*() const { return *_pos; }
    CircularBuffer& operator++() { ++_pos ; if (_pos == std::end(_container)) _pos = std::begin(_container); return *this; }
    CircularBuffer& operator--() { if (_pos == std::begin(_container)) _pos = std::end(_container); --_pos; return *this; }
};
#include <iostream>
#include <array>

int main()
{
    CircularBuffer<std::array<int,5>> buf;

    *buf = 1; ++buf;
    *buf = 2; ++buf;
    *buf = 3; ++buf;
    *buf = 4; ++buf;
    *buf = 5; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; ++buf;
    std::cout << *buf << " "; --buf;
    std::cout << *buf << " "; --buf;
    std::cout << *buf << " "; --buf;
    std::cout << *buf << " "; --buf;
    std::cout << *buf << " "; --buf;
    std::cout << *buf << " "; --buf;

    std::cout << std::endl;
}
g++ -std=c++17 -O2 -Wall -Wextra -pedantic -Werror
#ifndef RING_DEQUEUE_H
#define RING_DEQUEUE_H

#include <memory>
#include <type_traits>
#include <limits>

template <typename T, size_t N>
class ring_dequeue {
private:
    static_assert(N <= std::numeric_limits<size_t>::max() / 2 &&
                  N <= std::numeric_limits<size_t>::max() / sizeof(T),
                  "size of ring_dequeue is too large");

    using alloc_traits = std::allocator_traits<std::allocator<T>>;

public:
    using value_type = T;
    using reference = T&;
    using const_reference = const T&;
    using difference_type = ssize_t;
    using size_type = size_t;

    ring_dequeue() = default;

    // Disable copy and move constructors for now - if iterators are
    // implemented later, then those could be delegated to the InputIterator
    // constructor below (using the std::move_iterator adaptor for the move
    // constructor case).
    ring_dequeue(const ring_dequeue&) = delete;
    ring_dequeue(ring_dequeue&&) = delete;
    ring_dequeue& operator=(const ring_dequeue&) = delete;
    ring_dequeue& operator=(ring_dequeue&&) = delete;

    template <typename InputIterator>
    ring_dequeue(InputIterator begin, InputIterator end) {
        while (m_tailIndex < N && begin != end) {
            alloc_traits::construct(m_alloc, reinterpret_cast<T*>(m_buf) + m_tailIndex,
                                    *begin);
            ++m_tailIndex;
            ++begin;
        }
        if (begin != end)
            throw std::logic_error("Input range too long");
    }

    ring_dequeue(std::initializer_list<T> il) :
        ring_dequeue(il.begin(), il.end()) { }

    ~ring_dequeue() noexcept(std::is_nothrow_destructible<T>::value) {
        while (m_headIndex < m_tailIndex) {
            alloc_traits::destroy(m_alloc, elemPtr(m_headIndex));
            m_headIndex++;
        }
    }

    size_t size() const {
        return m_tailIndex - m_headIndex;
    }
    size_t max_size() const {
        return N;
    }

    bool empty() const {
        return m_headIndex == m_tailIndex;
    }
    bool full() const {
        return m_headIndex + N == m_tailIndex;
    }

    template <typename... Args>
    void emplace_front(Args&&... args) {
        if (full())
            throw std::logic_error("ring_dequeue full");
        bool wasAtZero = (m_headIndex == 0);
        auto newHeadIndex = wasAtZero ? (N - 1) : (m_headIndex - 1);
        alloc_traits::construct(m_alloc, elemPtr(newHeadIndex),
                                std::forward<Args>(args)...);
        m_headIndex = newHeadIndex;
        if (wasAtZero)
            m_tailIndex += N;
    }
    void push_front(const T& x) {
        emplace_front(x);
    }
    void push_front(T&& x) {
        emplace_front(std::move(x));
    }

    template <typename... Args>
    void emplace_back(Args&&... args) {
        if (full())
            throw std::logic_error("ring_dequeue full");
        alloc_traits::construct(m_alloc, elemPtr(m_tailIndex),
                                std::forward<Args>(args)...);
        ++m_tailIndex;
    }
    void push_back(const T& x) {
        emplace_back(x);
    }
    void push_back(T&& x) {
        emplace_back(std::move(x));
    }

    T& front() {
        if (empty())
            throw std::logic_error("ring_dequeue empty");
        return *elemPtr(m_headIndex);
    }
    const T& front() const {
        if (empty())
            throw std::logic_error("ring_dequeue empty");
        return *elemPtr(m_headIndex);
    }
    void remove_front() {
        if (empty())
            throw std::logic_error("ring_dequeue empty");
        alloc_traits::destroy(m_alloc, elemPtr(m_headIndex));
        ++m_headIndex;
        if (m_headIndex == N) {
            m_headIndex = 0;
            m_tailIndex -= N;
        }
    }
    T pop_front() {
        T result = std::move(front());
        remove_front();
        return result;
    }

    T& back() {
        if (empty())
            throw std::logic_error("ring_dequeue empty");
        return *elemPtr(m_tailIndex - 1);
    }
    const T& back() const {
        if (empty())
            throw std::logic_error("ring_dequeue empty");
        return *elemPtr(m_tailIndex - 1);
    }
    void remove_back() {
        if (empty())
            throw std::logic_error("ring_dequeue empty");
        alloc_traits::destroy(m_alloc, elemPtr(m_tailIndex - 1));
        --m_tailIndex;
    }
    T pop_back() {
        T result = std::move(back());
        remove_back();
        return result;
    }

private:
    alignas(T) char m_buf[N * sizeof(T)];
    size_t m_headIndex = 0;
    size_t m_tailIndex = 0;
    std::allocator<T> m_alloc;

    const T* elemPtr(size_t index) const {
        if (index >= N)
            index -= N;
        return reinterpret_cast<const T*>(m_buf) + index;
    }
    T* elemPtr(size_t index) {
        if (index >= N)
            index -= N;
        return reinterpret_cast<T*>(m_buf) + index;
    }
};

#endif