我一直使用resize(),因为我不能使用reserve(),因为它会出现错误:vector subscript out of range。当我阅读关于resize()和reserve()的区别的信息时,我看到像reserve()设置可分配的最大元素数,但resize()是我们目前拥有的。在我的代码中,我知道最大元素数,但reserve()对我没有任何有用的东西。那么,我该如何利用reserve()呢?
capacity()
返回)和一个大小(由size()
返回)。第一个表示向量可以容纳多少元素,第二个表示它当前容纳了多少元素。
resize
改变大小,而reserve
仅改变capacity
。vector
中,但不想初始化它们-这就是reserve
的用例。假设您的向量之前为空;那么,在进行任何insert
或push_back
操作之前,直接调用reserve()
,您当然不能直接访问与您保留空间相同的元素数量,因为您正在尝试访问尚未初始化的元素; size
仍然为0。因此,该向量仍然为空;但是,如果您以这种方式选择保留容量,使其大于或等于向量可能达到的最大大小,则可以避免昂贵的重新分配;同时,您还将避免resize
会进行(在某些情况下代价高昂的)每个向量元素的初始化。resize
,您可以说:使向量容纳给定大小的元素;将超过旧大小的索引的元素进行初始化,或者删除超出给定新大小的元素。请注意,reserve
函数永远不会影响当前向量中的元素(除非需要重新分配空间以更改其存储位置 - 但不会影响元素值或数量!),这意味着如果一个向量的大小当前大于您传递给同一向量的reserve
函数的参数,则reserve
将不起任何作用。reserve() 是一个用于 std::vector 的 性能优化。
一个典型的 std::vector 实现会在第一次 push_back() 时保留一些内存,例如4个元素。当第5个元素被推入时,向量必须被重新调整大小:新的内存必须被分配(通常是将大小加倍),向量的内容必须被复制到新位置,并删除旧内存。
当向量持有大量元素时,这会成为昂贵的操作。例如当您 push_back() 第2^24+1个元素时,就需要复制1600万个元素才能添加一个元素。
如果您事先知道元素的数量,可以使用 reserve()
预留计划 push_back()
的元素数。在这种情况下,不需要进行昂贵的复制操作,因为内存已经为所需数量预留。
相比之下,resize() 更改vector中的元素数量。
如果没有添加任何元素并使用 resize(20)
,那么现在将可以访问20个元素。分配的内存量也会增加到一个实现相关的值。
如果添加了50个元素并使用resize(20)
,则最后30个元素将从向量中删除并不再可访问。这不一定会更改分配的内存,但这也可能是与实现相关的。
resize(n)会为n个对象分配内存并进行默认初始化。
reserve()会分配内存但不进行初始化。因此,reserve不会改变size()返回的值,但它会改变capacity()的结果。
在 underscore_d 的评论后编辑。
描述了在 VS2015 中如何实现函数。
此错误对话框仅存在于 DEBUG 模式下,当定义 #if _ITERATOR_DEBUG_LEVEL == 2
时。在 RELEASE 模式下我们没有任何问题。我们通过return (*(this->_Myfirst() + _Pos)
获得当前值,因此不需要size
值:
reference operator[](size_type _Pos)
{ // subscript mutable sequence
#if _ITERATOR_DEBUG_LEVEL == 2
if (size() <= _Pos)
{ // report error
_DEBUG_ERROR("vector subscript out of range");
_SCL_SECURE_OUT_OF_RANGE;
}
#elif _ITERATOR_DEBUG_LEVEL == 1
_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
#endif /* _ITERATOR_DEBUG_LEVEL */
return (*(this->_Myfirst() + _Pos));
}
resize
和reserve
之间的区别仅在于resize()
函数中this->_Mylast()
值的更改。
reserve()
调用_Reallocate
。
resize()
调用_Reserve
,该函数调用_Reallocate
,然后resize()
还会更改this->_Mylast()
的值:this->_Mylast() += _Newsize - size();
,这在size
计算中使用(请参见最后一个函数)。 void resize(size_type _Newsize)
{ // determine new length, padding as needed
if (_Newsize < size())
_Pop_back_n(size() - _Newsize);
else if (size() < _Newsize)
{ // pad as needed
_Reserve(_Newsize - size());
_TRY_BEGIN
_Uninitialized_default_fill_n(this->_Mylast(), _Newsize - size(),
this->_Getal());
_CATCH_ALL
_Tidy();
_RERAISE;
_CATCH_END
this->_Mylast() += _Newsize - size();
}
}
void reserve(size_type _Count)
{ // determine new minimum length of allocated storage
if (capacity() < _Count)
{ // something to do, check and reallocate
if (max_size() < _Count)
_Xlen();
_Reallocate(_Count);
}
}
void _Reallocate(size_type _Count)
{ // move to array of exactly _Count elements
pointer _Ptr = this->_Getal().allocate(_Count);
_TRY_BEGIN
_Umove(this->_Myfirst(), this->_Mylast(), _Ptr);
_CATCH_ALL
this->_Getal().deallocate(_Ptr, _Count);
_RERAISE;
_CATCH_END
size_type _Size = size();
if (this->_Myfirst() != pointer())
{ // destroy and deallocate old array
_Destroy(this->_Myfirst(), this->_Mylast());
this->_Getal().deallocate(this->_Myfirst(),
this->_Myend() - this->_Myfirst());
}
this->_Orphan_all();
this->_Myend() = _Ptr + _Count;
this->_Mylast() = _Ptr + _Size;
this->_Myfirst() = _Ptr;
}
void _Reserve(size_type _Count)
{ // ensure room for _Count new elements, grow exponentially
if (_Unused_capacity() < _Count)
{ // need more room, try to get it
if (max_size() - size() < _Count)
_Xlen();
_Reallocate(_Grow_to(size() + _Count));
}
}
size_type size() const _NOEXCEPT
{ // return length of sequence
return (this->_Mylast() - this->_Myfirst());
}
但是reserve
存在一些问题:
end()
将等于begin()
23.2.1通用容器要求
5:
end()
返回一个迭代器,它是容器的超出末尾值。
iterator end() _NOEXCEPT
{ // return iterator for end of mutable sequence
return (iterator(this->_Mylast(), &this->_Get_data()));
}
即 _Mylast()
将等于 _Myfirst()
at()
会生成一个 out_of_range
异常。23.2.3 序列容器
17:
成员函数 at()
提供了对容器元素的边界检查访问。如果 n >= a.size(),则 at()
抛出 out_of_range
异常。
使用 resize
:
使用reserve
和手动设置#define _ITERATOR_DEBUG_LEVEL 0
:
resize
默认构造任何新元素,并允许访问它们的索引。reserve
不会初始化新容量,任何尝试在没有对其使用emplace
、push
或resize
进行初始化之前访问新分配的元素占位符的操作都是UB。严格的.at()
不允许这样做,这应该暗示您不能使用operator[]
。详细阐述一个实现中当前发生的事情并不有用,而且可能只会误导读者。 - underscore_d
insert
或push_back
(就像没有进行“reserve”一样),但您将避免重新分配,并且还将避免resize
触发的额外初始化...现在清楚了吗? - codeling