【问题标题】:How do you copy the contents of an array to a std::vector in C++ without looping?如何在不循环的情况下将数组的内容复制到 C++ 中的 std::vector?
【发布时间】:2010-09-20 12:47:03
【问题描述】:

我有一个值数组,这些值从程序的不同部分传递给我的函数,我需要存储这些值以供以后处理。由于我不知道在处理数据之前我的函数会被调用多少次,所以我需要一个动态存储结构,所以我选择了std::vector。我不想对push_back 的所有值单独执行标准循环,如果我可以使用类似于memcpy 的内容将其全部复制,那就太好了。

【问题讨论】:

    标签: c++ stl vector copy


    【解决方案1】:

    这里有很多答案,几乎所有人都能完成工作。

    但是有一些误导性的建议!

    以下是选项:

    vector<int> dataVec;
    
    int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    unsigned dataArraySize = sizeof(dataArray) / sizeof(int);
    
    // Method 1: Copy the array to the vector using back_inserter.
    {
        copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
    }
    
    // Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
    {
        dataVec.reserve(dataVec.size() + dataArraySize);
        copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
    }
    
    // Method 3: Memcpy
    {
        dataVec.resize(dataVec.size() + dataArraySize);
        memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
    }
    
    // Method 4: vector::insert
    {
        dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
    }
    
    // Method 5: vector + vector
    {
        vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
        dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
    }
    

    长话短说,方法 4 使用 vector::insert 是 bsruth 场景的最佳选择。

    这里有一些血腥的细节:

    方法 1 可能是最容易理解的。只需从数组中复制每个元素并将其推入向量的后面。唉,它很慢。因为有一个循环(隐含在复制功能中),所以每个元素都必须单独处理;由于我们知道数组和向量是连续的块,因此无法提高性能。

    方法 2 是对方法 1 的建议性能改进;只需在添加之前预先保留数组的大小。对于大型数组,这可能会有所帮助。然而,这里最好的建议是永远不要使用保留,除非分析表明您可以得到改进(或者您需要确保您的迭代器不会失效)。 Bjarne agrees。顺便说一句,我发现这种方法在大多数情况下执行最慢,尽管我正在努力全面解释为什么它经常明显比方法 1 慢... p>

    方法 3 是老派的解决方案 - 在问题上扔一些 C!对 POD 类型运行良好且快速。在这种情况下,需要调用 resize,因为 memcpy 在向量范围之外工作,并且无法告诉向量其大小已更改。除了是一个丑陋的解决方案(字节复制!)之外,请记住,这只能用于 POD 类型。我永远不会使用这个解决方案。

    方法 4 是最好的方法。它的含义很明确,它(通常)是最快的,并且适用于任何对象。将此方法用于此应用程序没有任何缺点。

    方法 5 是对方法 4 的调整 - 将数组复制到向量中,然后附加它。不错的选择 - 通常快速且清晰。

    最后,您知道可以使用向量代替数组,对吧?即使函数需要 c 样式的数组,您也可以使用向量:

    vector<char> v(50); // Ensure there's enough space
    strcpy(&v[0], "prefer vectors to c arrays");
    

    希望能帮助到那里的人!

    【讨论】:

    • 您不能安全且可移植地引用“&dataArray[dataArraySize]”——它取消了对过去指针/迭代器的引用。相反,您可以说 dataArray + dataArraySize 来获取指针,而不必先取消引用它。
    • @Drew:是的,你可以,至少在 C 语言中是这样。定义&amp;expr 不计算expr,它只计算它的地址。最后一个元素之后的指针 one 也是完全有效的。
    • 你试过用方法 2 做方法 4 吗?即在插入之前保留空间。似乎如果数据量很大,多次插入将需要多次重新分配。因为我们先验地知道大小,所以我们可以在插入之前进行重新分配。
    • @MattyT 方法 5 的意义何在?为什么要制作数据的中间副本?
    • 我个人宁愿从自动衰减到指针的数组中获利:dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize); - 对我来说似乎更清楚。也无法从方法 5 中获得任何东西,只是看起来效率很低——除非编译器能够再次优化向量。
    【解决方案2】:

    如果你在得到数组和数组大小后可以构造向量,你可以说:

    std::vector<ValueType> vec(a, a + n);
    

    ...假设a 是您的数组,n 是它包含的元素数。否则,std::copy() w/resize() 会成功。

    我会远离 memcpy(),除非您可以确定这些值是普通旧数据 (POD) 类型。

    另外,值得注意的是,这些都没有真正避免 for 循环——这只是你是否必须在代码中看到它的问题。 O(n) 运行时性能对于复制值是不可避免的。

    最后,请注意,对于大多数 STL 算法来说,C 风格的数组是完全有效的容器——原始指针等同于 begin(),而 (ptr + n) 等同于 end()

    【讨论】:

    • 循环调用 push_back 不好的原因是,如果数组足够长,你可能会强制向量多次调整大小。
    • @bradtgmurray:我认为我上面建议的“两个迭代器”向量构造函数的任何合理实现都会首先在两个迭代器上调用 std::distance() 以获得所需的元素数量,然后分配只有一次。
    • @bradtgmurray:即使 push_back() 也不会太糟糕,因为向量的指数增长(又名“摊销常数时间”)。我认为在最坏的情况下,运行时间只会差 2 倍。
    • 如果向量已经存在,一个 vec.clear(); vec.insert(vec.begin(), a, a + n);也可以。那么你甚至不需要 a 是一个指针,只需要一个迭代器,并且向量赋值将是失败的(和 C++/STL 方式)。
    • 无法构造时的另一种选择是assign:vec.assign(a, a+n),它比复制和调整大小更紧凑。
    【解决方案3】:

    如果您所做的只是替换现有数据,那么您可以这样做

    std::vector<int> data; // evil global :)
    
    void CopyData(int *newData, size_t count)
    {
       data.assign(newData, newData + count);
    }
    

    【讨论】:

    • 简单易懂,绝对是最快的解决方案(它只是幕后的 memcpy)。
    • deta.assign 比 data.insert 快吗?
    【解决方案4】:

    std::copy 就是你要找的东西。

    【讨论】:

      【解决方案5】:

      由于我只能编辑自己的答案,因此我将根据我的问题的其他答案做出综合答案。感谢所有回答的人。

      使用std::copy,这仍然在后台迭代,但您不必输入代码。

      int foo(int* data, int size)
      {
         static std::vector<int> my_data; //normally a class variable
         std::copy(data, data + size, std::back_inserter(my_data));
         return 0;
      }
      

      使用普通的memcpy。这可能最适合用于基本数据类型(即 int),但不适用于更复杂的结构或类数组。

      vector<int> x(size);
      memcpy(&x[0], source, size*sizeof(int));
      

      【讨论】:

      • 我打算推荐这种方法。
      • 如果您提前知道向量的大小而不使用 back_inserter,那么预先调整向量的大小可能会更有效。
      • 你可以添加 my_data.reserve(size)
      • 请注意,在内部这正是您想要避免的。它不是复制位,它只是循环并调用 push_back()。我猜你只是想避免输入代码?
      • Wjy 不使用向量构造函数来复制数据?
      【解决方案6】:
      int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//source
      
      unsigned dataArraySize = sizeof(dataArray) / sizeof(int);
      
      std::vector<int> myvector (dataArraySize );//target
      
      std::copy ( myints, myints+dataArraySize , myvector.begin() );
      
      //myvector now has 1,2,3,...10 :-)
      

      【讨论】:

      • 虽然欢迎使用此代码 sn-p,并且可能会提供一些帮助,但它会是 greatly improved if it included an explanation of howwhy 这解决了问题。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人!请edit您的答案添加解释,并说明适用的限制和假设。
      • 等等,myints 是什么?
      • 我猜这个例子来自cplusplus.com/reference/algorithm/copy,在那里你可以找到myints :)
      【解决方案7】:

      避免使用 memcpy,我说。除非你真的必须这样做,否则没有理由搞乱指针操作。此外,它仅适用于 POD 类型(如 int),但如果您正在处理需要构造的类型,它将失败。

      【讨论】:

      • 也许这应该是对其他答案之一的评论,因为您实际上并没有提出解决方案。
      【解决方案8】:

      还有一个答案,因为那个人说“我不知道我的函数会被调用多少次”,你可以像这样使用向量插入方法将值数组附加到向量的末尾:

      vector<int> x;
      
      void AddValues(int* values, size_t size)
      {
         x.insert(x.end(), values, values+size);
      }
      

      我喜欢这种方式,因为向量的实现应该能够根据迭代器类型和类型本身优化插入值的最佳方式。你有点回复stl的实现了。

      如果您需要保证最快的速度并且您知道您的类型是 POD 类型,那么我会推荐 Thomas 回答中的 resize 方法:

      vector<int> x;
      
      void AddValues(int* values, size_t size)
      {
         size_t old_size(x.size());
         x.resize(old_size + size, 0);
         memcpy(&x[old_size], values, size * sizeof(int));
      }
      

      【讨论】:

        【解决方案9】:

        除了上面介绍的方法之外,您还需要确保使用 std::Vector.reserve()、std::Vector.resize() 或按大小构造向量,以确保您的向量具有其中有足够的元素来保存您的数据。否则,您将破坏内存。对于 std::copy() 或 memcpy() 都是如此。

        这就是使用vector.push_back()的原因,你不能写超过vector的末尾。

        【讨论】:

        • 如果您使用的是 back_inserter,则无需预先保留要复制到的向量的大小。 back_inserter 执行 push_back()。
        【解决方案10】:

        假设您知道向量中的项目有多大:

        std::vector<int> myArray;
        myArray.resize (item_count, 0);
        memcpy (&myArray.front(), source, item_count * sizeof(int));
        

        http://www.cppreference.com/wiki/stl/vector/start

        【讨论】:

        • 那不依赖于 std::vector 的实现吗?
        • 太可怕了!您正在填充数组两次,一次使用 '0',然后使用正确的值。只需这样做: std::vector myArray(source, source + item_count);并相信您的编译器会生成 memcpy!
        • 相信你的编译器会产生 __memcpy_int_aligned;那应该更快
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-26
        • 2010-11-12
        • 1970-01-01
        相关资源
        最近更新 更多