我理解使用“保留”以避免不必要的重新分配(有效STL的第14项)是一个好习惯:
std::vector<int> v1;
v1.reserve(1000);
for (int i = 0; i < 1000; i++)
v1.push_back(i);
调用assign时是否适用相同的规则?
std::vector<int> v2;
//v2.reserve(v1.size()); // Better to do this?
v2.assign(v1.begin(), v1.end());
答案 0 :(得分:6)
如果v1
是std::vector
,你根本不需要它,因为编译器/ stl知道v2
中会有多少项(并且会reserve
1}}在复制实际数据之前所需的数量。)
但是,对于一般情况,如果输入容器(reserve
)不知道有多少项,那么事先v1
所需的金额可能是有意义的,并且你有手边的数字。
答案 1 :(得分:6)
是否致电reserve
取决于:
让我们按顺序拿下3分。
1)迭代器类型
assign
方法需要两个必须至少符合InputIterator
模型的迭代器。问题是这个模型代表纯粹的源(比如来自网络的字节):你可以消耗两次它的东西。结果,给定两个InputIterator
,在不提取数据的情况下计算它们之间的距离是不可能的(除非您根本不想要数据,但这不是分配的内容),所以你不能首先“保留”。
事实表明std::distance
至少需要FowardIterator
。
2)实施质量
我不认为标准实际上要求“更好”的迭代器(至少模型为ForwardIterator
),assign
的实现两次遍历范围。在受内存带宽限制的计算中(想象一下读取磁带上的信息,倒带时间非常慢),这实际上会更昂贵。
然而,许多实现(例如libc ++,见下文)将专门化assign
,以便在ForwardIterator
出现时,它首先调用std::distance
以在必要时保留适当的内存量
注意:顺便说一下,这同样适用于质量插入。
3)维护负担
我会注意到尽管可能获益,但你(可能是在不知不觉中)在这里复制信息。
size_t size = std::distance(begin, end);
if (begin != end) ++begin; // new line
v.reserve(size);
v.assign(begin, end);
看看新行的外观突然使代码略微不正确?并不是说它不会起作用,但据称优化不再那么正确:你现在保留太多了!
我个人认为我的标准库实现是正确的。写作他们的人比我更有经验。
如果确实是您应用程序中确定的瓶颈,您可以随时尝试。只需编写一个reserve_and_assign
方法,使其显而易见,并衡量它是否更好。
供参考,这里是libc ++实现,taken here:
template <class _Tp, class _Allocator>
template <class _InputIterator>
typename enable_if
<
__is_input_iterator <_InputIterator>::value &&
!__is_forward_iterator<_InputIterator>::value,
void
>::type
vector<_Tp, _Allocator>::assign(_InputIterator __first, _InputIterator __last)
{
clear();
for (; __first != __last; ++__first)
push_back(*__first);
}
template <class _Tp, class _Allocator>
template <class _ForwardIterator>
typename enable_if
<
__is_forward_iterator<_ForwardIterator>::value,
void
>::type
vector<_Tp, _Allocator>::assign(_ForwardIterator __first, _ForwardIterator __last)
{
typename iterator_traits<_ForwardIterator>::difference_type __new_size = _VSTD::distance(__first, __last);
if (static_cast<size_type>(__new_size) <= capacity())
{
_ForwardIterator __mid = __last;
bool __growing = false;
if (static_cast<size_type>(__new_size) > size())
{
__growing = true;
__mid = __first;
_VSTD::advance(__mid, size());
}
pointer __m = _VSTD::copy(__first, __mid, this->__begin_);
if (__growing)
__construct_at_end(__mid, __last);
else
this->__destruct_at_end(__m);
}
else
{
deallocate();
allocate(__recommend(static_cast<size_type>(__new_size)));
__construct_at_end(__first, __last);
}
}