因此,在线评判中的每个问题都需要存储一堆值然后处理它们的基本工作。我通常使用std :: vector作为这些,但我担心使用push_back()
存储每个元素对性能不利。
目前我这样做:
vector<int> vec;
int x;
for(int i=0;i<10;i++)
{
cin>>x;
vec.push_back(x);
}
但是我觉得如果这会更好
vector<int> vec;
int x;
vec.reserve(10);
for(int i=0;i<10;i++)
cin>>vec[i];
哪个更合适? ,性能有什么不同吗? 并且让我说我有充分的理由避免使用C风格的数组,是的,我事先知道了矢量的初始大小。
答案 0 :(得分:2)
阅读std::vector的文档。您会注意到reserve
仅适用于
请求更改容量
请求向量容量至少足以包含n个元素。
如果n大于当前矢量容量,则该函数会使容器重新分配其存储,将其容量增加到n(或更大)。
在所有其他情况下,函数调用不会导致重新分配,矢量容量也不会受到影响。
此功能对矢量大小没有影响,不能改变其元素。
因此,您无法在reserve
之后存储元素,首先需要resize
。
vec.resize(10);
for(int i=0;i<10;i++)
cin>>vec[i];
顺便说一句,当你想要推送一些元素并且你知道有多少元素时,你应该更好vec.reserve(10)
之前:
vec.reserve(10);
for(int i=0;i<10;i++) {
cin>>x;
vec.push_back(x);
}
关于性能问题:在您的特定情况下,您可能不关心(因为I / O比vector
函数慢得多)。一般情况下,您应该在很多reserve
之前尝试push_back
,因为push_back
会在内部执行一些reserve
等效项(重新分配一些未知的实现特定数量)如果空间不够。也许不使用reserve
可能会定期触发重新分配,您可能希望避免这种情况。
我敢打赌,在合适的vec[i]
之后以resize
存储是最快的,因为operator []
被记录为:
BTW,大多数STL实现(包括来自GCC的返回对向量容器中位置n处元素的引用。
类似的成员函数vector :: at与此操作符函数具有相同的行为,除了对vector :: at进行绑定检查,并通过抛出out_of_range异常来通知请求的位置是否超出范围。
可移植程序永远不应该使用超出范围的参数n来调用此函数,因为这会导致未定义的行为。
libstdc++
...)都是某种程度上的自由软件,因此您可以研究它们的源代码以了解究竟是做了什么,并且您可以随时进行基准测试。
答案 1 :(得分:2)
首先,小心,在你的第二个例子中有一个明显的错误:reserve
扩展了向量的容量,即你可以放入其中的元素数量而不执行向量重新分配,但不影响其逻辑大小。因此,如果执行reserve
,则仍需执行push_back
,否则您将在循环中访问逻辑上不存在的元素。您可能想要的是resize
,它将逻辑大小和容量扩展到所请求的大小。
现在,来表演:
push_back
vs reserve
+ push_back
:大致相同(如果副本便宜)即使没有reserve
,向量也会在分摊的常量时间内达到所需的容量(实际上在O(log N)时间内,无论如何都会在循环的O(N)中隐藏)。
好的,如果您已经确定了矢量将采用的大小,它将避免重新分配,但不要跳过箍来确定要保留多少。
异常:具有昂贵的复制构造函数的类型(在C ++ 11中移动构造函数)。如果要存储复制/移动成本昂贵的对象,则需要避免重新分配,因此reserve
可能会有所帮助(尽管通常用指针存储这些类型,避免出现问题)。
resize
+ operator[]
:对于“简单”类型我真正看到的是,在处理简单类型(通常是POD,或者通常使用极其简单的构造函数/赋值运算符)时,性能稍微提高,是事先做resize
,然后通过[]
运营商。
这避免了push_back
的额外复杂性,resize
必须检查容量并增加“逻辑大小”;通过下标运算符OTOH进行赋值,在优化后解析为少数汇编指令。
当然,您不希望push_back
并在具有复杂类型时进行分配,其中初始默认构造和分配抵消了{{1}}必须执行的轻量簿记。
答案 2 :(得分:2)
我想我更喜欢选项C:
const int num = 10;
std::vector<int> vec;
vec.reserve(num);
std::copy_n(std::istream_iterator<int>(std::cin), num, std::back_inserter(vec));
...但是如果你对代码很短,那么你可能更喜欢D:
std::vector<int> vec(num);
std::copy_n(std::istream_iterator<int>(std::cin), num, vec.begin());
就我个人而言,我并不喜欢这样(它有点不那么惯用,IMO)但它的效果非常好,而且显然更短。
答案 3 :(得分:0)
vector
在capacity
不足时会自动增长,因此push_back
如果您事先了解尺寸,则reserve
不是一个好主意。
您的第二种方法更好,但存在问题:resize
和vector<int> vec;
vec.resize(10); // actually change the size, so your vec is now of size 10
for (int i = 0; i < 10; ++i) // pre-increment is better
cin >> vec[i];
不一样。
您有两种方法可以更有效地完成这项工作:
1
vector<int> vec;
vec.reserve(10); // only enlarge the capacity, but size is still 0
int x;
for (int i = 0; i < 10; ++i) // pre-increment is better
{
cin >> x;
vec.push_back(x); // would not trigger any memory reallocation, performance is fine
}
2
capacity
更详细:
size
和capacity
:
vector
表示为vector
分配的内存,因此当超过此数量时,整个size
将被复制到新的内存空间,这样效率不高,所以请尝试尽量避免这种情况。
<=
是当前元素的数量,它是push_back
容量,只要您执行reserve
,此数字就会增加1。
resize
和reserve
:
capacity
更改vector
,您告诉capacity
&#34;保留&#34;有多少空间可供使用,所以如果你能知道你的大小,那就调用这个函数来确保你可以避免重新分配内存。此功能仅更改size
,但不会更改resize
。
vector
立即更改size
&#39; size
,它还可能导致内存分配。如果您的新size
大于旧版{{1}},则新添加的元素将默认初始化。
请参阅此处获取完整信息:
答案 4 :(得分:0)
如果你想存储比普通int更复杂的对象,最快的解决方案应该是使用emplace_back()来创建对象(在向量内),如果你的编译器支持那么。预先留出足够的空间可以防止多次重新分配。
class Widget
{
public:
Widget(const std::string& name, int x, int y);
};
std::vector<Widget> widgets;
widgets.reserve(nWidgets);
widgets.emplace_back("w1", 0, 0);
widgets.emplace_back("w2", 100, 20);
// ...