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


121

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

Answers:


116

如果可以在获得数组和数组大小之后构造向量,则可以说:

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

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

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

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

最后,请注意,C样式数组对于大多数STL算法都是完全有效的容器-原始指针等于begin(),而(ptr + n)等于end()


4
循环和调用push_back不好的原因是,如果数组足够长,您可能会强制向量重新调整大小。
bradtgmurray

@bradtgmurray:我认为我上面建议的“两个迭代器”矢量构造函数的任何合理实现都将首先在两个迭代器上调用std :: distance()以获得所需数量的元素,然后仅分配一次。
德鲁·霍尔

4
@bradtgmurray:即使是push_back()也不会太糟糕,因为向量呈指数增长(又称“摊销固定时间”)。我认为在最坏的情况下,运行时间只会恶化2倍左右。
德鲁厅

2
如果向量已经存在,则为vec.clear();。vec.insert(vec.begin(),a,a + n); 也会工作。然后,您甚至不需要将a用作指针,只需一个迭代器,并且向量分配将是常规的失败(以及C ++ / STL方式)。
MP24

6
无法构造时的另一种替代方法是分配vec.assign(a, a+n),它比复制和调整大小更紧凑。
mMontu

209

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

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

以下是选项:

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可能是最容易理解的。只需复制数组中的每个元素,然后将其推入向量的背面即可。las,很慢。因为有一个循环(带有复制功能),所以每个元素都必须单独处理;我们知道数组和向量是连续的块,因此无法提高性能。

方法2是对方法1的建议性能改进;只需在添加之前预先保留阵列的大小即可。对于大型阵列,这可能会有所帮助。但是,这里最好的建议是永远不要使用储备,除非分析表明您可能会有所改进(或者您需要确保迭代器不会失效)。 Bjarne同意。顺便说一句,我发现这个方法执行的最慢的大部分时间,虽然我竭力要全面地解释为什么它是定期显著慢于法1 ...

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

方法4是最好的方法。它的意思很清楚,它(通常)是最快的,并且适用于任何对象。在此应用程序中使用此方法没有任何弊端。

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

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

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

希望能帮助到那里的人!


6
您不能安全且方便地引用“&dataArray [dataArraySize]”,因为它正在引用过去的指针/迭代器。相反,您可以说dataArray + dataArraySize来获取指针,而不必先取消对其的引用。
德鲁厅

2
@Drew:是的,至少在C语言中可以。它定义为&expr不求值expr,它仅计算它的地址。和一个指向一个过去的最后一个元素是完全有效的,太。
罗兰·伊利格

2
您是否尝试过用2做方法4?即在插入之前保留空间。看起来,如果数据量很大,则多次插入将需要多次重新分配。因为我们知道先验的大小,所以我们可以在插入之前进行重新分配。
豪尔赫·雷涛

2
@MattyT方法5的意义是什么?为什么要中间复制数据?
罗斯兰

2
我个人更希望从自动衰减到指针的数组中获利:dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);–对我而言似乎更清晰。也不能从方法5获得任何东西,只能看起来效率很低–除非编译器能够再次优化向量。
阿空加瓜

37

如果您要做的是替换现有数据,则可以执行此操作

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

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

1
简单易懂,绝对是最快的解决方案(这只是幕后花絮)。
唐·斯科特

deta.assign是否比data.insert快?
吉姆(Jim)


10

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

使用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));

我打算推荐这种方法。
mmocny,

如果您提前知道矢量的大小,并且不使用back_inserter,则最有可能更有效地提前调整矢量的大小。
路加福音

您可以添加my_data.reserve(size)
David Nehme'Nov

请注意,这在内部确实在执行您似乎要避免的操作。它不是复制位,而是循环并调用push_back()。我想您只是想避免输入代码?
mmocny,

1
为什么不使用vector构造函数复制数据?
马丁·约克

3

我说,避免使用memcpy。除非确实需要,否则没有理由搞乱指针操作。另外,它仅适用于POD类型(如int),但如果要处理需要构造的类型,则将失败。


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

3
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 :-)

2
尽管此代码段是受欢迎的,并且可能会提供一些帮助,但是如果它包含有关如何以及为什么可以解决此问题的说明,则可以大大改善。请记住,您将来会为读者回答问题,而不仅仅是现在问的人!请编辑您的答案以添加说明,并指出适用的限制和假设。
Toby Speight

4
等待,是什么myints
mavavilj

2

另一个答案是,由于该人说“我不知道我的函数将被调用多少次”,因此您可以使用向量插入方法,将值数组附加到向量的末尾:

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

1

除了上面介绍的方法外,您还需要确保使用std :: Vector.reserve(),std :: Vector.resize()或将向量构造为适当大小,以确保向量中包含足够的元素它可以保存您的数据。如果没有,您将破坏内存。std :: copy()或memcpy()都是如此。

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


如果使用的是back_inserter,则无需预先保留要复制到的向量的大小。back_inserter执行push_back()。
John Dibling

0

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

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的实现吗?
ReaperUnreal,

那太糟了!您将数组填充两次,一个以0填充,然后以适当的值填充。只是做:std :: vector <int> myArray(source,source + item_count); 并相信您的编译器会产生memcpy!
克里斯·杰斐逊

相信您的编译器产生__memcpy_int_aligned; 速度应该更快
MSalters
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.