我想知道vector
的push_back
和insert
函数有哪些区别。
它们之间是否有结构上的差异?
它们之间是否存在非常大的性能差异?
我想知道vector
的push_back
和insert
函数有哪些区别。
它们之间是否有结构上的差异?
它们之间是否存在非常大的性能差异?
最大的区别在它们的功能上。push_back
总是将新元素放在vector
的末尾,而insert
允许您选择新元素的位置。这会影响性能。vector
元素仅在必须增加其长度时才移动到内存中,因为分配给它的内存太少了。另一方面,insert
强制移动新元素选定位置后的所有元素,以为其腾出空间。这就是为什么insert
可能比push_back
效率低的原因。
push_back
一样高效。 - Matthieu M.insert
实际上是否在end()
,因此insert
中将会有比push_back
更多的分支,除非编译器能够发现它们实际上是不需要的。 - Yakk - Adam Nevraumont这些函数有不同的目的。 vector::insert
允许您在 vector
中指定的位置插入一个对象,而vector::push_back
只会将对象添加到末尾。请看以下示例:
using namespace std;
vector<int> v = {1, 3, 4};
v.insert(next(begin(v)), 2);
v.push_back(5);
// v now contains {1, 2, 3, 4, 5}
您可以使用insert
执行与push_back
相同的操作,使用v.insert(v.end(), value)
。
除了 push_back(x)
可能会比 insert(x, end())
(可能稍微更快)执行相同的操作之外,还有几个需要了解这些函数的重要事项:
push_back
仅存在于 BackInsertionSequence
容器中 - 例如,它不存在于 set
中。因为 push_back()
保证始终在末尾添加。FrontInsertionSequence
,并且它们具有 push_front
。这适用于 deque
,但不适用于 vector
。insert(x, ITERATOR)
来自于 InsertionSequence
,这对于 set
和 vector
是共通的。这样你可以将 set
或 vector
作为多次插入的目标。但是,set
还有另一个 insert(x)
,它实际上做了相同的事情(这里的第一个 insert 在 set
中只是从不同的迭代器开始加速搜索适当的位置的特性,在此情况下未使用)。关于最后一种情况的说明是,如果您要在循环中添加元素,则执行 container.push_back(x)
和 container.insert(x, container.end())
将实际上执行相同的操作。但是,如果您先获取此 container.end()
,然后在整个循环中使用它,则这种情况不成立。
例如,您可能会冒险编写以下代码:
auto pe = v.end();
for (auto& s: a)
v.insert(s, pe);
这将会把整个a
逆序复制到v
向量中,但只有在你运气好到没有因为扩展而重新分配向量(你可以通过先调用reserve()
来防止这种情况)的时候才能生效;如果你运气不好,你将会遇到所谓的UndefinedBehavior(tm)。理论上,这是不允许的,因为每次添加新元素时,向量的迭代器都被认为无效。
如果你使用以下方式:
copy(a.begin(), a.end(), back_inserter(v);
它将按照原始顺序在末尾复制a
到v
中,这不会导致迭代器失效的风险。
[编辑] 我之前让这段代码看起来像这样,但这是一个错误,因为inserter
实际上维护了迭代器的有效性和推进:
copy(a.begin(), a.end(), inserter(v, v.end());
因此,这段代码也会按照原始顺序添加所有元素,没有任何风险。
inserter
会维护迭代器的前进和有效性。我需要编辑帖子 :) - Ethouris由于没有实际的性能数据,我不情愿地编写了一些代码来生成它。请记住,我编写这段代码是因为我想知道“我应该使用push_back多个单元素,还是使用insert?”。
#include <iostream>
#include <vector>
#include <cassert>
#include <chrono>
using namespace std;
vector<float> pushBackTest()
{
vector<float> v;
for(int i =0;i<10000000;i++)
{
// Using a for-loop took 200ms more (in the output)
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
v.push_back(8);
v.push_back(9);
}
return v;
}
vector<float> insertTest()
{
vector<float> v;
for(int i =0;i<10000000;i++)
{
v.insert(v.end(), {0,1,2,3,4,5,6,7,8,9});
}
return v;
}
int main()
{
std::chrono::steady_clock::time_point start = chrono::steady_clock::now();
vector<float> a = pushBackTest();
cout<<"pushBackTest: "<<chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start).count()<<"ms"<<endl;
start = std::chrono::steady_clock::now();
vector<float> b = insertTest();
cout<<"insertTest: "<<chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - start).count()<<"ms"<<endl;
assert(a==b);
return 0;
}
输出:
pushBackTest: 5544ms
insertTest: 3402ms
由于好奇心占据了我的时间,我进行了类似的测试,但是只添加了一个数字而不是多个数字。 因此,这两个新函数是:
vector<float> pushBackTest()
{
vector<float> v;
for(int i =0;i<10000000;i++)
{
v.push_back(1);
}
return v;
}
vector<float> insertTest()
{
vector<float> v;
for(int i =0;i<10000000;i++)
{
v.insert(v.end(), 1);
}
return v;
}
输出:
pushBackTest: 452ms
insertTest: 615ms
所以,如果你想添加一批元素,插入是更快的选择,否则就使用 push_back。同时要记住,push_back 只能将元素放在末尾。
我在之前的评论中没有看到,但这很重要:
如果我们想要添加一个新元素到给定的向量中,而新向量的大小(包括新元素)超过当前向量容量,它将导致自动重新分配已分配的存储空间。 由于内存分配是我们希望最小化的操作,因此它会在 push_back 和 insert 中以相同的方式增加容量(对于一个有 n 个元素的向量,将增加大约 n/2)。
因此从内存效率的角度来说,可以放心使用任何你喜欢的方法。 例如:
std::vector<int> test_Insert = { 1,2,3,4,5,6,7 };
std::vector<int> test_Push_Back = { 1,2,3,4,5,6,7 };
std::cout << test_Insert.capacity() << std::endl;
std::cout << test_Push_Back.capacity() << std::endl;
test_Insert.push_back(8);
test_Push_Back.insert(test_Push_Back.end(), 8);
std::cout << test_Insert.capacity() << std::endl;
std::cout << test_Push_Back.capacity() << std::endl;
7
7
10
10
insert
可以在任何位置执行,并支持其他功能,如范围操作。而push_back
更方便用于在末尾添加元素。 - chris