如何在C ++向量中存储内容

时间:2014-08-13 05:37:59

标签: c++ vector std

因此,在线评判中的每个问题都需要存储一堆值然后处理它们的基本工作。我通常使用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风格的数组,是的,我事先知道了矢量的初始大小。

5 个答案:

答案 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 []被记录为:

  

返回对向量容器中位置n处元素的引用。

     

类似的成员函数vector :: at与此操作符函数具有相同的行为,除了对vector :: at进行绑定检查,并通过抛出out_of_range异常来通知请求的位置是否超出范围。

     

可移植程序永远不应该使用超出范围的参数n来调用此函数,因为这会导致未定义的行为。

BTW,大多数STL实现(包括来自GCClibstdc++ ...)都是某种程度上的自由软件,因此您可以研究它们的源代码以了解究竟是做了什么,并且您可以随时进行基准测试。

答案 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)

vectorcapacity不足时会自动增长,因此push_back如果您事先了解尺寸,则reserve不是一个好主意。

您的第二种方法更好,但存在问题:resizevector<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

更详细:

sizecapacity

vector表示为vector分配的内存,因此当超过此数量时,整个size将被复制到新的内存空间,这样效率不高,所以请尝试尽量避免这种情况。

<=是当前元素的数量,它是push_back容量,只要您执行reserve,此数字就会增加1。

resizereserve

capacity更改vector,您告诉capacity&#34;保留&#34;有多少空间可供使用,所以如果你能知道你的大小,那就调用这个函数来确保你可以避免重新分配内存。此功能仅更改size,但不会更改resize

vector立即更改size&#39; size,它还可能导致内存分配。如果您的新size大于旧版{{1}},则新添加的元素将默认初始化。

请参阅此处获取完整信息:

http://www.cplusplus.com/reference/vector/vector/?kw=vector

答案 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);
// ...