【问题标题】:Build a vector as a subset of another one without copying构建一个向量作为另一个向量的子集而不复制
【发布时间】:2018-12-13 01:34:36
【问题描述】:

v 成为一个向量。我希望w 成为索引fromto 之间v 的子集。我可以这样做

std::vector<T> w(v.begin() + from, v.begin() + to);

但是,我以后不打算使用v。因此,我不需要在fromto 之间复制数据。我需要创建一个指向v.begin() + from 并且长度为to - from 的向量。 v 使用的其余内存应该被释放。请注意,如果 v 被重新定义,我很好(如果我愿意,我可以在之后切换到 w)。

有可能吗?

【问题讨论】:

  • 关于你是否应该使用std::deque的问题,这完全取决于你如何使用这个向量。请记住,双端队列的存储在内存中是不连续的。此外,双端队列使用的内存量通常大于向量使用的内存量,这削弱了您关于需要释放范围之外的部分内存的观点。使用双端队列,您对此没有太多控制权。
  • @paddy 是的,我意识到没有足够的信息来回答这个额外的问题。我删除了SHould I use a deque frmo 我的帖子。仅供参考,我的意思是对这些向量进行很多子集化,有时需要复制,有时则不需要。我还需要快速(如果可能,恒定时间)查找。
  • 我怀疑vector是否可行:vector的内部只是一大块连续内存,我怀疑你有什么办法只释放部分内存块。想想看:如果你有int* foo = new int [1000];,有什么办法可以释放,比如说,从foo[0]foo[50]?恕我直言,这是不可能的
  • 如果您编写自己的内存分配器,可以分割一个块并返回它周围的部分,这是可能的。尽管可能无法将该分配器与向量一起使用。当然,如果针对像这样的非常具体的要求进行工程设计,那可能是您需要采用的方式,以及您自己的容器来包装该功能。请注意,与使用标准分配器和少量复制相比,这可能会导致更多的内存碎片。
  • @Remi.b 您可以在我的 deque 答案中看到一些您可能已经知道的 deque 详细信息。 paddy 对是否需要连续分配的担忧是正确的,但性能应该几乎完全相同,因为查找是常数时间,只有两个间接,仅比向量多 1,并且取决于双端队列的桶的大小,您在前后存储桶上保留的最大浪费内存量。我敢说,在我现在能想到的几乎所有 PC 情况下,我都会使用双端队列。

标签: c++ vector containers move


【解决方案1】:

这应该用矢量来解决问题。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v{ 11, 22, 33, 44, 55, 66, 77, 88, 99 };
    constexpr size_t from = 3;
    constexpr size_t to = 7;

    if(to < v.size() - 1)
        v.erase(v.begin() + to, v.end());

    if(from > 0)
        v.erase(v.begin(), v.begin() + from - 1);

    v.shrink_to_fit();

    for(auto i : v)
        cout << i << ' ';
    cout << endl;
}

【讨论】:

  • 在开始擦除元素时,实际上是将元素复制到第一个位置。它不会移动开始迭代器。
  • @Remi.b 你有这方面的参考吗?
  • @MichaelSurette 尝试使用移动赋值运算符输出某些内容的类
【解决方案2】:

如果您想使用vector,则无法避免复制。如果你想确保未使用的内存被释放,你可以这样做:

std::vector<T> w(v.begin() + from, v.begin() + to);
std::vector<T>().swap(v);

【讨论】:

  • 这并不能保证释放未使用的内存。最好的选择是w.clear(); w.shrink_to_fit();,但这也不能保证(没有办法保证)
  • @M.M,您的建议不附带保证。我的建议确保v 是一个新的空vector,并且v 的先前内容超出了分号的范围。
  • 都不保证(没办法保证)
  • @M.M,我想我们不同意这一点。
  • @SidS: 不保证新的空向量的容量为 0。
【解决方案3】:

Should be available soon. 同时,您的选择是:

  1. 使用std::string&lt;T&gt; 而不是vector,因此使用std::string_view

  2. 制作你自己的视图类。说,

    template<class Iterator> class view {
        Iterator b, e;
    public:
        view(Iterator b, Iterator e): b(b), e(e) {}
    
        auto &operator[](std::size_t i) { return *(b[i]); }
        auto const &operator[](std::size_t i) const { return *(b[i]); }
        auto begin() const { return b; }
        auto end() const { return e; }
        auto size() const { return e - b; }
    };
    

【讨论】:

  • 即使有范围,它也不会释放内存。我猜他需要另一个容器。因为为了这个目的,他也可以简单地保存整个向量和迭代器。恕我直言
  • 哦,我错过了剩下的记忆部分。 0_o
【解决方案4】:

您可能应该使用 std::deque,并弹出从 begin() 到 begin() +front 以及从 end() - 到 end() 的所有元素。这将释放未使用的内存,减去第一个桶的前面和最后一个桶的末尾的一小块。 std::deque 非常高效,因为它将其内容存储在数组桶中。它不仅与向量一样高效,而且在实践中可能已经足够好,因为它只是额外的 1 级间接。 大致就是这样的区别:

  • std::vector [i] -> 返回缓冲区[i]
  • std::deque[i] -> buckets[i] -> return bucket[i];

参考:https://en.cppreference.com/w/cpp/container/deque

这取决于您增长和缩小向量的程度,但双端队列会缩小和增长,而不会复制任何元素,它只是存储桶分配/释放。因此在某些情况下它的性能可能比向量高得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 2015-11-06
    • 1970-01-01
    • 2021-01-23
    • 1970-01-01
    • 2016-07-08
    • 1970-01-01
    相关资源
    最近更新 更多