C++ vector's insert & push_back difference
我想知道vector的push_back和insert函数之间的区别是什么。
有结构上的差异吗?
在性能上有很大的不同吗?
-
insert可以在任何地方执行,并包括范围支持之类的其他功能。 push_back更加方便地添加到末尾。
最大的区别在于它们的功能。 push_back总是在vector的末尾放置一个新元素,并且insert允许您选择新元素的位置。这会影响性能。 vector元素仅在需要增加其长度时才在内存中移动,因为为其分配的内存太少了。另一方面,insert强制将所有元素移动到新元素的选定位置之后。您只需要为此放置一个位置。这就是为什么insert效率通常可能不及push_back的原因。
-
另一方面,在末尾插入应该与push_back一样有效。
-
嗯,几乎一样有效-代码需要确定insert实际上在end()上,因此insert中的分支要比push_back中的分支多,除非编译器可以弄清楚他们实际上并不需要。
-
我认为额外的比较可以忽略不计。
-
@TomerW人们选择C或C ++可以压缩每个机器周期,以提高性能,以至于有时直接在ASM中编写...每个"如果"都算在内,这不是Java。
-
@Cesar这不是您选择语言的原因... cpp可以比c更快,反之亦然...地狱,在某些情况下,甚至Java对其运行时优化也可以比c更快。
-
@TomerW ho,为什么?请赐教。
-
@Cesar态度很好,尽管id仍然可以回答。 1. C ++是面向对象的。2. C ++还有许多其他的库和资源可供使用。 3. C ++模板引擎可以使您的代码更高效。 4. C ++编写起来更有趣(注释,中间范围变量等)。5.异常机制可使您的设计与众不同。 6.名称空间可以使您的代码更具可伸缩性。还有更多,但我想你明白了。
-
@Cesar BTW:至于在某些情况下,托管性能超过了本机性能...请阅读有关JIT优化的信息...如今从stackoverflow.com/questions/145110/c-performance-vs-java-c开始,Java已经RT-Java也引入了确定性和抢先式调度。希望你现在看到了光:)
-
@TomerW谢谢
-
@TomerW也许它不值钱,因为它仅在玩具软件上运行,但仍然是一个有趣的禁令:Benchmarksgame.alioth.debian.org
功能具有不同的用途。 vector::insert允许您在vector中的指定位置插入对象,而vector::push_back只是将对象粘在末端。请参见以下示例:
1 2 3 4 5
| 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和v.insert(v.end(), value)来执行与push_back相同的任务。
除了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),它实际上具有相同的作用(在set中的第一次插入仅意味着通过从不同的迭代器开始加快搜索适当位置的功能-在这种情况下不使用此功能)。
关于最后一种情况,请注意,如果要在循环中添加元素,则执行container.push_back(x)和container.insert(x, container.end())将会有效地完成相同的操作。但是,如果先获得此container.end()然后在整个循环中使用它,那将不是正确的。
例如,您可能会冒险以下代码:
1 2 3
| auto pe = v.end();
for (auto& s: a)
v.insert(pe, v); |
这将以相反的顺序有效地将整个a复制到v向量中,只有在您有幸没有重新分配向量进行扩展时(可以通过首先调用reserve()来防止这种情况);如果您不太幸运,则会得到所谓的UndefinedBehavior(tm)。从理论上讲,这是不允许的,因为每次添加新元素时,矢量的迭代器都被视为无效。
如果您这样做:
1
| copy(a.begin(), a.end(), back_inserter(v); |
它将按原始顺序在v的末尾复制a,这不存在迭代器无效的风险。
[编辑]我以前使这段代码看起来是这样,这是一个错误,因为inserter实际上保持了迭代器的有效性和先进性:
1
| copy(a.begin(), a.end(), inserter(v, v.end()); |
因此,此代码还将以原始顺序添加所有元素,而没有任何风险。
-
您关于std :: inserter被类矢量容器"冒险"的说法是不正确的。实际上,其旨在消除这些风险。没有反向顺序,没有未定义的行为。参见例如fluentcpp.com/2017/10/06/stl-inserter-iterators-进行故障分析。
-
你是对的。我只是想使代码简短而不是将其扩展为扩展的手动循环,所以实际上并非如此,因为inserter保持了迭代器的先进性和有效性。不得不编辑帖子:)