有几种创建方法vector
with n
当您事先不知道元素的数量时,我什至会展示一些填充向量的方法。
但首先
不该做什么
std::vector<Entry> phone_book;
for (std::size_t i = 0; i < n; ++i)
{
phone_book[i] = entry; // <-- !! Undefined Behaviour !!
}
默认构造的向量(如上例所示)创建一个空向量。访问向量范围之外的元素是未定义的行为。并且不要指望会得到一个很好的例外。未定义的行为意味着任何事情都可能发生:程序可能会崩溃,或者看起来可以工作,或者可能以一种不稳定的方式工作。请注意,使用reserve
不会改变向量的实际大小,即你无法访问向量大小之外的元素,即使你为它们保留了。
现在分析一些选项
默认向量+push_back
(次优)
std::vector<Entry> phone_book;
for (std::size_t i = 0; i < n; ++i)
{
phone_book.push_back(entry);
}
这样做的缺点是当您推回元素时会发生重新分配。这意味着内存分配、元素移动(或复制,如果它们是不可移动的,或者对于 c++11 之前的版本)和内存释放(带有对象销毁)。这种情况很可能会发生不止一次n
相当大。值得注意的是,它保证“摊余常数”push_back
这意味着它不会在每次之后进行重新分配push_back
。每次重新分配都会以几何级数增加大小。进一步阅读:std::vector 和 std::string 重新分配策略 https://stackoverflow.com/questions/21352323/stdvector-and-stdstring-reallocation-strategy
当您事先不知道尺寸并且甚至没有对尺寸的估计时,请使用此功能。
“计算 T 的默认插入实例” ctor 以及后续分配(不建议)
std::vector<Entry> phone_book(n);
for (auto& elem : phone_book)
{
elem = entry;
}
这不会导致任何重新分配,但所有n
元素将最初默认构造,然后为每次推送复制。这是一个很大的缺点,并且对性能的影响很可能是可衡量的。 (这对于基本类型不太明显)。
不要使用它,因为几乎每种情况都有更好的选择。
“计算元素的副本”ctor(受到推崇的)
std::vector<Entry> phone_book(n, entry);
这是最好的使用方法。当您在构造函数中提供所需的所有信息时,它将进行最有效的分配+赋值。这有可能产生无分支代码,如果满足以下条件,则使用矢量化指令进行赋值:Entry
有一个简单的复制构造函数。
默认向量+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();
不会发生重新分配,并且对象只会构造一次,直到超出保留容量。更好的选择push_back
can be emplace_back
.
如果您有粗略的尺寸近似值,请使用此值。
储备价值没有神奇的公式。针对您的特定场景使用不同的值进行测试,以获得应用程序的最佳性能。最后你可以使用shrink_to_fit http://en.cppreference.com/w/cpp/container/vector/shrink_to_fit.
默认向量+std::fill_n
and 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);
如果您需要在创建向量后填充或添加元素,请使用此选项。
默认向量+std::generate_n
and std::back_inserter
(对于不同的entry
物体)
Entry entry_generator();
std::vector<Entry> phone_book;
std::generate_n(std::back_inserter(phone_book), n, [] { return entry_generator(); });
如果每个都可以使用它entry
是不同的并且从生成器获得
初始化列表(奖励)
由于这已经成为一个如此重要的答案,超出了问题所提出的范围,如果我没有提到初始化列表构造函数,我将被解雇:
std::vector<Entry> phone_book{entry0, entry1, entry2, entry3};
在大多数情况下,当您有一小部分用于填充向量的初始值时,这应该是默认的首选构造函数。
一些资源:
std::vector::vector(构造函数) https://en.cppreference.com/w/cpp/container/vector/vector
std::vector::insert https://en.cppreference.com/w/cpp/container/vector/insert
标准算法库 https://en.cppreference.com/w/cpp/algorithm (with std::generate https://en.cppreference.com/w/cpp/algorithm/generate std::generate_n https://en.cppreference.com/w/cpp/algorithm/generate_n std::fill https://en.cppreference.com/w/cpp/algorithm/fill std::fill_n https://en.cppreference.com/w/cpp/algorithm/fill_n etc.)
std::back_inserter https://en.cppreference.com/w/cpp/iterator/back_inserter