发布于2026-07-19 阅读(0)
扫一扫,手机访问
扩容必须分配新内存并逐个移动/拷贝构造,因为vector底层是连续原始缓冲区,new[]没办法就地延展,realloc对非平凡类型也不适用。直接拿memcpy去拷string这类带指针、引用计数的对象,结果就是双释放或悬垂指针。

malloc + memcpy 或 new + 手动拷贝?关键原因在于 std::vector 的内存是连续原始缓冲区,没有“就地扩容”这个选项。C++ 的 new[] 分配的是固定大小的堆块,不像 realloc 还能尝试原地延展(而且 realloc 对含有非平凡构造函数的对象根本行不通)。所以唯一安全的路径是:分配新内存 → 逐个调用移动/拷贝构造 → 析构旧对象 → 释放旧内存。
不少人图省事,直接 memcpy 原始字节——对 int 这种平凡类型没毛病,但一旦碰上 std::string 这类内部带指针、引用计数的对象,就会引发双释放或悬垂指针,调试起来非常头疼。
T(std::move(old[i])) 或 T(old[i]) 构造到新地址std::allocator_traits::construct 或手动调用构造函数,不能跳过push_back 触发扩容时,如何避免重复计算容量?每次 push_back 都检查 size_ == capacity_ 是必须的,但扩容倍数策略直接影响性能。用固定加法(比如每次+1)会导致 O(n²) 复杂度,用乘性增长(如 ×1.5 或 ×2)才能摊还为 O(1)。
注意:×2 简单但可能浪费内存;×1.5 更省空间,不过需要浮点运算或整数技巧(比如 new_cap = size_ + size_ / 2)。实际中 std::vector 通常用 ×2。
new_capacity,不要在循环里反复计算n,直接分配 n 容量,别先分配 0 再 push n 次T 是 move-only 类型(如 std::unique_ptr)?手写 vector 必须区分 T 是否支持移动。如果只用 copy,对 unique_ptr 编译不过;如果盲目用 move,对 int 虽无害但冗余。标准做法是优先 move,fallback 到 copy。
核心是用 std::is_nothrow_move_constructible_v 和 std::is_copy_constructible_v 做 trait 判断,再配合 std::move 或 static_cast。
new (ptr) T(std::move(old_elem)),不是 new (ptr) T(old_elem)obj.~T()std::allocator 来管理内存?继承是可以继承,但没必要,而且容易误用。std::allocator 只负责 raw 内存分配/释放(allocate/deallocate),不负责对象构造/析构。真正要复用的是它的接口约定,而不是继承它。
更轻量、更可控的做法是封装一个 allocator_type 类型别名,默认用 std::allocator,并在内部调用 alloc_.allocate(n) 和 alloc_.deallocate(ptr, n)。
operator new 支持对齐参数)std::allocator 可无缝对接自定义 allocator(比如 arena allocator)sizeof(T) —— 让 allocator 自己算真实手写时最常被忽略的,是异常安全边界:只要新内存分配成功,但某个元素构造抛异常,就必须立即析构已构造的新元素,并释放新内存,然后重新抛出——这个 try-catch 块不能少,也不能只包构造不包析构。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8