提问人:Yola 提问时间:6/30/2015 更新时间:6/30/2015 访问量:326
为什么在转换/复制矢量时有这么多复制
why so many copying while transforming/copying vector
问:
为什么有这么多电话要复制缺点,我预计只有最后九个?甚至根本不适合返回值优化。
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
答:
10赞
juanchopanza
6/30/2015
#1
你的向量必须增长几倍。每次这样做时,它都会分配一个更大的内存块,并复制原始元素。vc
您可以通过使用 保留足够的空间来阻止它这样做。std::vector::reserve
vector<C> vc;
vc.reserve(vi.size());
评论
1赞
Yola
6/30/2015
是的,谢谢,这是真的,但为什么它增长得这么慢,但不是每次都增长两倍?是不是有点傻?
3赞
juanchopanza
6/30/2015
@Yola 它的增长接近两倍。确切的因素取决于实施。
0赞
Fabio Fracassi
6/30/2015
许多实现使用更接近黄金比例 (~1.62) 而不是 2 的增长因子的主要原因是,这可以防止内存碎片。有关更深入的解释,请参阅此答案
4赞
Vlad from Moscow
6/30/2015
#2
从程序输出中可以看出,当向量添加新元素时,内存将被重新分配,并且向量中已经存在的元素被复制到新位置。
您可以在运行算法之前保留足够的内存,以避免内存重新分配。
vector<C> vc;
vc.reserve( vi.size() );
在这种情况下,可以避免对复制构造函数进行冗余调用。
但这还不是全部:)
类 C 有一个转换构造函数
C(int i) : _i(i) {}
它允许通过将使用 lambda 表达式的算法的调用替换为算法而不使用 lambda 表达式来简化向量元素的创建。例如vc
std::transform
std::copy
std::copy( vi.begin(), vi.end(), std::back_inserter( vc ) );
但即便如此,这还不是故事的全部:)
当您使用任一或然后使用时,将使用两个构造函数:带有参数的构造函数和复制结构器。std::transform
std::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 ); } );
在这两种情况下,将仅调用带有参数的构造函数,避免调用复制构造函数。
自己检查一下:)
评论
0赞
Yola
6/30/2015
谢谢你这么详细的回答。
1赞
Vlad from Moscow
6/30/2015
@Yola我希望这是一个有趣且有用的答案:)
评论