Step 04
reserve 与扩容
把本页「可粘贴代码」整段(从 template 到函数结尾)替换作业纸里对应的 TODO。不要贴进 class 体内。本步搜索 TODO(step 04),替换: uninitialized_relocate_n、recommend_capacity、reallocate、reserve、shrink_to_fit。参考答案在 参考答案页;编译问题见 常见问题。
Step 04 — reserve 与扩容
reserve(n) 的契约:
- 保证之后至少还能在不重分配的情况下装到
n个元素。 - 不改变
size(),不构造新元素。所以Vector<NoDefault>可以reserve(100)。 n <= capacity()时什么都不做(迭代器、指针保持有效)。
几何增长(1 → 2 → 4 → 8 …)放在 emplace_back 里用 recommend_capacity,不要放在 reserve 里。v.reserve(5) 就申请 5 个槽,和 std::vector 一致。
搬迁策略(强异常安全)
旧缓冲区上的元素要搬到新缓冲区。规则:
T的移动构造是noexcept(或T根本不能拷贝)→ 移动。- 否则 → 拷贝。拷贝抛异常时源对象还在,旧 vector 完好。
如果移动可能抛、又只能移动(unique_ptr),抛了就只能给基本保证:新缓冲区的半成品要析构掉,旧缓冲区尽量别动。uninitialized_move_n 在抛之前不会 deallocate 旧内存,所以旧 vector 仍可用。
在作业纸里改哪里
搜索 TODO(step 04),替换:uninitialized_relocate_n、recommend_capacity、reallocate、reserve、shrink_to_fit。
可粘贴代码
template <typename T, typename Allocator>
void Vector<T, Allocator>::uninitialized_relocate_n(T* dest, T* src, size_type n) {
if constexpr (std::is_nothrow_move_constructible_v<T> ||
!std::is_copy_constructible_v<T>) {
uninitialized_move_n(dest, src, n);
} else {
uninitialized_copy_n(dest, src, n);
}
}template <typename T, typename Allocator>
typename Vector<T, Allocator>::size_type
Vector<T, Allocator>::recommend_capacity(size_type min_cap) const {
const size_type max_n = max_size();
if (min_cap > max_n) {
throw std::length_error("mini::Vector::reserve");
}
size_type new_cap = 1;
if (capacity_ > 0) {
if (capacity_ > max_n / 2) {
new_cap = max_n;
} else {
new_cap = capacity_ * 2;
}
}
return new_cap < min_cap ? min_cap : new_cap;
}capacity_ * 2 可能溢出,所以先和 max_n / 2 比较。
template <typename T, typename Allocator>
void Vector<T, Allocator>::reallocate(size_type new_cap) {
T* new_data = allocate_n(new_cap);
try {
uninitialized_relocate_n(new_data, data_, size_);
} catch (...) {
deallocate_n(new_data, new_cap);
throw;
}
T* old_data = data_;
size_type old_cap = capacity_;
size_type old_size = size_;
data_ = new_data;
capacity_ = new_cap;
destroy_range(old_data, old_data + old_size);
deallocate_n(old_data, old_cap);
}顺序很重要:先在新内存上构造完毕,再析构旧元素。中途抛异常时旧缓冲区还活着。
template <typename T, typename Allocator>
void Vector<T, Allocator>::reserve(size_type new_cap) {
if (new_cap <= capacity_) {
return;
}
reallocate(new_cap);
}template <typename T, typename Allocator>
void Vector<T, Allocator>::shrink_to_fit() {
if (size_ == capacity_) {
return;
}
if (size_ == 0) {
deallocate_n(data_, capacity_);
data_ = nullptr;
capacity_ = 0;
return;
}
reallocate(size_);
}验收
cmake --build build
./build/vector_tests --gtest_filter='Step04*'ReserveKeepsExistingElements 用了 push_back,要 Step 05 一起绿。现在能过的是“空 vector reserve 之后 size 仍为 0、capacity 变大”这一类。
本步验收
cmake --build build
./build/vector_tests --gtest_filter='Step04*'