初始化C ++向量以及其他容器的大小有什么好处(如果有的话)?有没有理由不只使用默认的no-arg构造函数?
基本上,
之间是否存在显着的性能差异vector<Entry> phone_book;
和
vector<Entry> phone_book(1000);
这些例子来自Bjarne Stroustrup的C ++编程语言第三版。如果这些容器应该总是用一个尺寸进行初始化,那么有一个很好的方法可以确定一个好的尺寸开始吗?
答案 0 :(得分:23)
使用vector
元素创建n
有三种方法:
(A):
vector<Entry> phone_book;
for (i = 0; i < n; ++i) {
phone_book.push_back(entry);
}
(B):
vector<Entry> phone_book(n);
for (i = 0; i < n; ++i) {
phone_book[i] = entry;
}
(C):
vector<Entry> phone_book;
phone_book.reserve(n);
for (i = 0; i < n; ++i) {
phone_book.push_back(entry);
}
(A)的缺点是,当你推回元素时会发生重新分配。这意味着内存分配,元素移动(或者如果它们不可移动则复制,或者用于预先c ++ 11)和内存释放(使用对象销毁)。对n
相当大的人来说,这很可能不止一次。
(B)不会产生任何重新分配,但所有n
元素最初都是默认构造的,然后为每次推送复制。这是一个很大的缺点,对性能的影响是可以衡量的。 (这对于基本类型来说不太明显)
(C)是最好的使用方法。不会发生重新分配,对象只会构造一次。 push_back
的更好选择可以是emplace_back
。
如果您不知道高级尺寸,您可以做的是(A)。
如果您只是大致使用了大小(C)(更喜欢更大的猜测而不是更低的猜测),最后您可以使用shrink_to_fit。
答案 1 :(得分:7)
如果你知道大小是多少,那么你应该初始化它,以便只分配一次内存。如果您只是大致了解大小,那么您可以使用默认构造函数创建向量,然后保留大致正确的数量,而不是像上面那样分配存储。 e.g。
vector<Entry> phone_book();
phone_book.reserve(1000);
// add entries dynamically at another point
phone_book.push_back(an_entry);
编辑:
@juanchopanza提出了一个很好的观点 - 如果你想避免默认构造对象,那么如果你有一个移动构造函数或push_back
来直接构造,那么请保留并使用emplace_back
。
答案 2 :(得分:1)
当您很好地了解需要在向量中存储的元素数量时,可以初始化大小。如果您正在从数据库或其他源中检索数据,例如您知道其中包含1000个元素,则继续使用内部数组分配向量并保留那么多数据是有意义的。如果您事先不知道所需的大小是什么,那么随着时间的推移让矢量按需增长可能没问题。
正确的答案取决于您的应用程序及其特定用例。您可以根据需要测试性能并调整大小。通常情况下,只需让工作正常,然后再回过头来测试这些变化的影响。很多时候你会发现默认设置工作得很好。
答案 3 :(得分:0)
这是Bjarne Stroustrup的坏榜样。 而不是第二个定义
vector<Entry> phone_book(1000);
写
会好得多vector<Entry> phone_book;
phone_book.reserve( 1000 );
没有一般的“好方法”来确定一个好的尺寸开始。这取决于您拥有的有关任务的信息。但无论如何,如果您确定将新元素添加到向量中,则可以使用一些初始分配。