初始化 C++ 向量的大小
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25108854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Initializing the size of a C++ vector
提问by amhokies
What are the advantages (if any) of initializing the size of a C++ vector as well as other containers? Is there any reason to not just use the default no-arg constructor?
初始化 C++ 向量和其他容器的大小有什么优点(如果有的话)?有什么理由不只使用默认的无参数构造函数吗?
Basically, are there any significant performance differences between
基本上,两者之间是否存在显着的性能差异
vector<Entry> phone_book;
and
和
vector<Entry> phone_book(1000);
These examples come from The C++ Programming Language Third Edition by Bjarne Stroustrup. If these containers should always be initialized with a size, is there a good way to determine what a good size to start off would be?
这些示例来自 Bjarne Stroustrup 的 The C++ Programming Language Third Edition。如果这些容器应该总是用一个大小来初始化,有没有一个好方法来确定一个好的开始大小是多少?
回答by bolov
There are a few ways of creating a vector
with n
elements and I will even show some ways of populating a vector when you don't know the number of elements in advance.
有几种创建vector
带有n
元素的方法,当您事先不知道元素的数量时,我什至会展示一些填充向量的方法。
But first
但首先
what NOT to do
什么不该做
std::vector<Entry> phone_book;
for (std::size_t i = 0; i < n; ++i)
{
phone_book[i] = entry; // <-- !! Undefined Behaviour !!
}
The default constructed vector, as in the example above creates an empty vector. Accessing elements outside of the range of the vector is Undefined Behavior. And don't expect to get a nice exception. Undefined behavior means anything can happen: the program might crash or might seem to work or might work in a wonky way. Please note that using reserve
doesn't change the actual size of the vector, i.e. you can't access elements outside of the size of the vector, even if you reserved for them.
默认构造的向量,如上例所示,创建一个空向量。访问向量范围之外的元素是未定义行为。并且不要期望得到一个很好的例外。未定义的行为意味着任何事情都可能发生:程序可能会崩溃,或者看起来可以工作,或者可能以一种不稳定的方式工作。请注意,使用reserve
不会改变向量的实际大小,即您不能访问向量大小之外的元素,即使您为它们保留。
And now some options analyzed
现在分析了一些选项
default ctor + push_back
(suboptimal)
默认 ctor + push_back
(次优)
std::vector<Entry> phone_book;
for (std::size_t i = 0; i < n; ++i)
{
phone_book.push_back(entry);
}
This has the disadvantage that reallocations will occur as you push back elements. This means memory allocation, elements move (or copy if they are non-movable, or for pre c++11) and memory deallocation (with object destruction). This will most likely happen more than once for an n
decently big. It is worth noting that it is guaranteed "amortized constant" for push_back
which means that it won't do reallocations after each push_back
. Each reallocation will increase the size geometrically. Further read: std::vector and std::string reallocation strategy
这有一个缺点,即在您推回元素时会发生重新分配。这意味着内存分配、元素移动(或复制,如果它们不可移动,或对于 pre c++11)和内存释放(带有对象销毁)。对于一个n
体面的大公司来说,这很可能会发生不止一次。值得注意的是,它保证“摊销常数”,push_back
这意味着它不会在每个push_back
. 每次重新分配都会以几何方式增加大小。进一步阅读:std::vector 和 std::string 重新分配策略
Use this when you don't know the size in advance and you don't even have an estimate for the size.
当您事先不知道尺寸并且您甚至没有估计尺寸时,请使用此选项。
"count default-inserted instances of T" ctor with later assignments (not recommended)
“计数默认插入的 T 实例” ctor 稍后分配(不推荐)
std::vector<Entry> phone_book(n);
for (auto& elem : phone_book)
{
elem = entry;
}
This does not incur any reallocation, but all n
elements will be initially default constructed, and then copied for each push. This is a big disadvantage and the effect on the performance will most likely be measurable. (this is less noticeable for basic types).
这不会导致任何重新分配,但所有n
元素最初将默认构造,然后为每次推送复制。这是一个很大的缺点,对性能的影响很可能是可以衡量的。(这对于基本类型不太明显)。
Don't use this as there are better alternatives for pretty much every scenario.
不要使用它,因为几乎每种情况都有更好的替代方案。
"count copies of elements" ctor (recommended)
“计算元素的副本数”ctor (推荐)
std::vector<Entry> phone_book(n, entry);
This is the best method to use. As you provide all the information needed in the constructor, it will make the most efficient allocation + assignment. This has the potential to result in branchless code, with vectorized instructions for assignments if Entry
has a trivial copy constructor.
这是最好的使用方法。当您在构造函数中提供所需的所有信息时,它将进行最有效的分配 + 赋值。这有可能导致无分支代码,如果Entry
具有简单的复制构造函数,则具有用于赋值的向量化指令。
default ctor + reserve
+ push_back
(situational recommended)
默认的构造函数+ reserve
+ push_back
(情景推荐)
vector<Entry> phone_book;
phone_book.reserve(m);
while (some_condition)
{
phone_book.push_back(entry);
}
// optional
phone_book.shrink_to_fit();
No reallocation will occur and the objects will be constructed only once until you exceed the reserved capacity. A better choice for push_back
can be emplace_back
.
不会发生重新分配,并且对象只会构造一次,直到超出保留容量。更好的选择push_back
可以是emplace_back
。
Use this If you have a rough approximation of the size.
如果您对尺寸有一个粗略的估计,请使用它。
There is no magical formula for the reserve value. Test with different values for your particular scenarios to get the best performance for your application. At the end you can use shrink_to_fit
.
储备价值没有神奇的公式。针对您的特定场景使用不同的值进行测试,以获得应用程序的最佳性能。最后你可以使用shrink_to_fit
.
default ctor + std::fill_n
and std::back_inserter
(situational recommended)
默认 ctor +std::fill_n
和std::back_inserter
(情况推荐)
#include <algorithm>
#include <iterator>
std::vector<Entry> phone_book;
// at a later time
// phone_book could be non-empty at this time
std::fill_n(std::back_inserter(phone_book), n, entry);
Use this if you need to fill or add elements to the vector after its creation.
如果您需要在创建后向矢量填充或添加元素,请使用此选项。
default ctor + std::generate_n
and std::back_inserter
(for different entry
objects)
默认 ctor +std::generate_n
和std::back_inserter
(对于不同的entry
对象)
Entry entry_generator();
std::vector<Entry> phone_book;
std::generate_n(std::back_inserter(phone_book), n, [] { return entry_generator(); });
You can use this if every entry
is different and obtained from a generator
如果每个entry
都不同并且从生成器获得,则可以使用它
回答by sfjac
If you know ahead what the size is, then you should initialize it so that memory is only allocated once. If you only have a rough idea of the size, then instead of allocating the storage as above, you can create the vector with the default constructor and then reserve an amount that is approximately correct; e.g.
如果您事先知道大小是多少,那么您应该对其进行初始化,以便只分配一次内存。如果您只对大小有一个粗略的概念,那么您可以使用默认构造函数创建向量,然后保留一个大致正确的数量,而不是像上面那样分配存储空间;例如
vector<Entry> phone_book();
phone_book.reserve(1000);
// add entries dynamically at another point
phone_book.push_back(an_entry);
EDIT:
编辑:
@juanchopanza makes a good point - if you want to avoid default constructing the objects, then do reserve and use push_back
if you have a move constructor or emplace_back
to construct directly in place.
@juanchopanza 提出了一个很好的观点 - 如果您想避免默认构造对象,那么push_back
如果您有移动构造函数或emplace_back
直接在适当位置构造,请保留并使用。
回答by Tim Bish
You initialize the size when you have a good idea of the number of elements that you need to store in the vector. If you are retrieving data from database or other source for instance that you know has 1000 elements in it then it makes sense to go ahead and allocate the vector with an internal array that will hold that much data. If you don't know up front what the needed size will be then it might be ok to just let the vector grow as needed over time.
当您很好地了解需要存储在向量中的元素数量时,您可以初始化大小。如果您正在从数据库或其他来源检索数据,例如您知道其中有 1000 个元素,那么继续并使用将容纳这么多数据的内部数组分配向量是有意义的。如果您事先不知道所需的大小是多少,那么可以让向量随着时间的推移根据需要增长。
The right answer comes down to your application and its specific use case. You can test performance and tweak the sizing as needed. Usually its a good idea to just get things working and then go back and test the affects of these sorts of changes later. A lot of time you'll find that the defaults work just fine.
正确答案取决于您的应用程序及其特定用例。您可以根据需要测试性能并调整大小。通常,先让事情正常工作,然后再回去测试这些更改的影响,这是一个好主意。很多时候你会发现默认值工作得很好。
回答by Vlad from Moscow
It is a bad example of Bjarne Stroustrup. Instead of the second definitiona
这是 Bjarne Stroustrup 的一个坏例子。而不是第二个定义a
vector<Entry> phone_book(1000);
it would be much better to write
写出来会好很多
vector<Entry> phone_book;
phone_book.reserve( 1000 );
There is no general "good ways" to determine what a good size to start off would be. It depends on the information you possess about the task. But in any case you can use some initial allocation if you are sure that new elements will be added to the vector.
没有通用的“好方法”来确定开始时的合适尺寸。这取决于您拥有的有关任务的信息。但是在任何情况下,如果您确定将新元素添加到向量中,您都可以使用一些初始分配。