【问题标题】:why so many copying while transforming/copying vector为什么在转换/复制矢量时进行如此多的复制
【发布时间】:2015-06-30 09:46:33
【问题描述】:

为什么会有这么多电话复制缺点,我希望只有最后九个?甚至根本不适合返回值优化。

struct C
{
    int _i;
    C(int i) : _i(i) {}
    C(const C& other) { cout << "copy cons from " << other._i << " to " << _i << endl; _i = other._i; }
};
int _tmain(int argc, _TCHAR* argv[])
{
    vector<int> vi{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    vector<C> vc;
    transform(vi.begin(), vi.end(), back_inserter(vc), 
        [](int i) 
    { 
        return C(i); 
    });
}

输出:

copy cons from 1 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 5 to - 842150451
copy cons from 6 to - 842150451
copy cons from 1 to - 842150451
copy cons from 2 to - 842150451
copy cons from 3 to - 842150451
copy cons from 4 to - 842150451
copy cons from 5 to - 842150451
copy cons from 6 to - 842150451
copy cons from 7 to - 842150451
copy cons from 8 to - 842150451
copy cons from 9 to - 842150451

【问题讨论】:

    标签: c++ algorithm c++11 stl copy-constructor


    【解决方案1】:

    您的向量vc 必须增长几次。每次这样做时,它都会分配一个更大的内存块,并复制原始元素。

    您可以通过使用std::vector::reserve 保留足够的空间来阻止它这样做。

    vector<C> vc;
    vc.reserve(vi.size());
    

    【讨论】:

    • 是的,谢谢,这是真的,但为什么它增长如此缓慢,而不是例如每次乘以两倍?是不是有点傻?
    • @Yola 它以接近两倍的倍数增长。确切的因素取决于实施。
    • 许多实现使用比 2 更接近黄金比例 (~1.62) 的增长因子的主要原因是,这可以防止内存碎片。更深入的解释见this answer
    【解决方案2】:

    从程序输出中可以看出,当一个新元素被添加到向量中时,内存被重新分配,向量中已经存在的元素被复制到新的位置。

    您可以在运行算法之前预留足够的内存以避免内存重新分配。

    vector<C> vc;
    vc.reserve( vi.size() );
    

    在这种情况下,可以避免复制构造函数的冗余调用。

    但这不是完整的故事。:)

    C 类有一个转换构造函数

    C(int i) : _i(i) {}
    

    它允许通过将使用 lambda 表达式的算法 std::transform 的调用替换为不使用 lambda 表达式的算法 std::copy 来简化向量 vc 的创建元素。例如

    std::copy( vi.begin(), vi.end(), std::back_inserter( vc ) ); 
    

    但这还不是全部。:)

    当您使用std::transformstd::copy 时,将使用两个构造函数:带参数的构造函数和复制构造函数。

    您可以避免使用复制构造器并获得更有效的结果。简单地代替方法push_back,最好使用方法emplace_back

    这个方法怎么用?

    最简单的就是使用基于范围的for语句

    for ( int x : vi ) vc.emplace_back( x );
    

    足够清晰易读。

    如果你想使用标准算法,你可以编写

    std::for_each( vi.begin(), vi.end(), [&vc]( int x ) { vc.emplace_back( x ); } );
    

    在这两种情况下,只会调用带参数的构造函数,避免调用复制构造函数。

    自己检查一下。:)

    【讨论】:

    • 感谢您如此详尽的回答。
    • @Yola 我希望这是一个有趣且有用的答案。:)
    猜你喜欢
    • 1970-01-01
    • 2011-09-12
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 2015-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多