push_back()和resize(size()+ 1)之间有什么区别吗?

时间:2013-01-18 03:55:19

标签: c++ stdvector

可以通过以下任一方式扩展标准向量以容纳另一个成员:

std::vector<int> v;
v.push_back(1);

int os = v.size();
v.resize(os+1);
v[os] = 1;

除了使用push_back()的代码的简洁性外,还有其他差异吗?例如,一个比另一个更有效,还是在每种情况下分配不同的额外内存?

5 个答案:

答案 0 :(得分:8)

push_back将就地构造,这意味着它会调用复制构造函数。

resize将调用默认构造函数,然后v[os]将调用赋值运算符。

使用push_back


正如Lightness所说,两种情况下的分配是相同的。有关详细信息,请参阅他的回答。

以下是一个示例:http://stacked-crooked.com/view?id=43766666e5c72d282bd94c05e43e8897

答案 1 :(得分:5)

内存扩展

  

但他们是否以同样的方式扩展内存?

在C ++ 11(我已选中)中未明确指定,但

在这种情况下,

resize被定义为“附加到容器”,唯一的逻辑解释使其在分配方面等同于insertpush_back

容器实现的后端在其内存块扩展时通常会分配“超出需要”的所有情况 - 这是由标准不禁止它而不是标准强制执行的它,并没有任何措辞表明resize也不是这样。

最终,这完全取决于实现,但我会惊讶地看到在任何主流编译器上两种方法之间的内存分配规则存在差异。

This testcase显示了一个简单的例子:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> a, b;

    for(int i = 0; i != 100; ++i)
      a.push_back(0);
    for(int i = 0; i != 100; ++i)
      b.resize(i+1);

    std::cout << a.capacity() << " , " << b.capacity() << std::endl;
}

// Output from my GCC 4.7.2:
//  128 , 128

请注意,this one略有不同:

int main() {
    std::vector<int> a, b;

    for(int i = 0; i != 100; ++i)
      a.push_back(0);
    b.resize(100);

    std::cout << a.capacity() << " , " << b.capacity() << std::endl;
}

// Output from my GCC 4.7.2:
//  128 , 100

这两种方法之间的不是公平的测试,因为在后一个例子中,我们以不同的增量扩展内存,并且内存支持序列容器倾向于以倍数扩展


性能问题

无论如何,正如@Pubby所指出的那样,在resize情况下,你将进行隐式构造,然后进行显式赋值,如果你过早地进行优化,这是非最优的。

在现实世界中,真正的问题是使用错误的作业编写愚蠢的代码

答案 2 :(得分:3)

它们不具有可比性,您不应该根据性能做出决定,但据说性能可能会有很大差异,具体取决于实现。

标准要求push_back()分摊常量时间,这基本上意味着实现必须增加几何系列之后的对象的缓冲区(即,当增长时,新大小必须与之前的大小成比例因子F> 1)。

resize()没有这样的要求。事实上,一些实现假设如果你正在调用resize(),那是因为你更好地了解了向量的最终大小。考虑到这一点,这些实现会将缓冲区(如果需要)增加到您要求的大小,而不是遵循几何级数。这意味着遵循此机制的追加成本可以是O(N ^ 2),而不是push_back情况下的O(N)。

答案 3 :(得分:1)

是的,可能存在显着差异,尤其是在使用自定义数据类型时。观察:

#include <iostream>
#include <vector>

struct S
{
    S()
    {
        std::cout << "S()\n";
    }

    S(const S&)
    {
        std::cout << "S(const S&)\n";
    }

    S& operator = (const S&)
    {
        std::cout << "operator =\n";
        return *this;
    }

    ~S()
    {
        std::cout << "~S()\n";
    }
};

int main()
{
    std::vector<S> v1;

    std::cout << "push_back:\n";
    v1.push_back(S());

    std::vector<S> v2;

    std::cout << '\n' << "resize:\n";
    v2.resize(1);
    v2[0] = S();

    std::cout << "\nend\n"; // Ignore destructors after "end" (they're not pertinent to the comparison)
}

输出:

push_back:
S()
S(const S&)
~S()

resize:
S()
S(const S&)
~S()
S()
operator =
~S()

end
~S()
~S()

push_back FTW。

编辑:为了回应Orbit评论中的@Lightness Races,这当然只是一个愚蠢的例子。 S显然不是真正“有用”struct,如果删除所有打印语句,或者只是将S定义为struct S {};,那么编译器当然可以优化了很多。

,因为您何时在程序中使用难以置信的微不足道的数据类型?您将使用一些简单的数据类型,但有一天您也可能会使用一些非平凡的数据类型。这些非平凡的数据类型可能包含昂贵的构造函数,析构函数或赋值运算符(或者它们可能不是非常昂贵,但如果您反复使用resize而不是push_back)它们可能会相加,并且{ {1}}肯定是正确的选择。

答案 4 :(得分:0)

resize()可以在需要添加少量元素时更好地分配内存。

std::vector<int> v;
for(int i = 0; i < n; ++i)
    v.push_back(1);
or

int os = v.size();
v.resize(os.size() + n);
for(int i = 0; i < n; ++i)
    v[os + i] = 1;

但在这种情况下,最好使用

v.reserve(os.size() + n);

然后是push_back,它也会避免构造+赋值,就像你的情况一样