ChatGPT解决这个技术问题 Extra ChatGPT

如何在不循环的情况下将数组的内容复制到 C++ 中的 std::vector?

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


M
MattyT

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

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

以下是选项:

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 慢...

方法 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 中。它被定义为 &expr 不评估 expr,它只计算它的地址。最后一个元素之后的指针 one 也是完全有效的。
你试过用 2 做方法 4 吗?即在插入之前保留空间。似乎如果数据量很大,多次插入将需要多次重新分配。因为我们先验地知道大小,所以我们可以在插入之前进行重新分配。
@MattyT 方法5有什么意义?为什么要制作数据的中间副本?
我个人宁愿从自动衰减为指针的数组中获利:dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize); - 对我来说似乎更清楚。也无法从方法 5 中获得任何东西,只是看起来效率很低——除非编译器能够再次优化向量。
p
phoenix

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

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

...假设 a 是您的数组,而 n 是它包含的元素数。否则,std::copy() w/resize() 可以解决问题。

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

此外,值得注意的是,这些都没有真正避免 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),它比 copy & 更紧凑。调整大小。
T
Torlack

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

std::vector<int> data; // evil global :)

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}

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

std::copy 是您要查找的内容。


b
bsruth

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

使用 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 不使用向量构造函数来复制数据吗?
T
Toby Speight
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 :-)

虽然欢迎使用此代码片段,并且可能会提供一些帮助,但它会是关于 如何为什么 解决问题的greatly improved if it included an explanation。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人!请edit您的回答以添加解释,并说明适用的限制和假设。
等等,myints 是什么?
我猜这个示例来自 cplusplus.com/reference/algorithm/copy,您可以在其中找到 myints :)
S
Shane Powell

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

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));
}

A
Assaf Lavie

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


也许这应该是对其他答案之一的评论,因为您实际上并未提出解决方案。
T
Thomas Jones-Low

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

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


如果您使用的是 back_inserter,则不需要预先保留要复制到的向量的大小。 back_inserter 执行 push_back()。
T
Thomas Jones-Low

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

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;那应该更快