C++raw_storage_iterator原始存储迭代器
C++ raw_storage_iterator:原始存储迭代器的原理与实践
在 C++ 标准库中,迭代器是容器与算法之间的桥梁。绝大多数迭代器负责访问已构造对象的内存;而 raw_storage_iterator 则是一个特例——它不操作已有对象,而是将未初始化的原始内存“伪装”成可写入的目标,配合构造函数完成对象的就地构建。这一机制在内存池、自定义分配器及高性能容器实现中具有独特价值。
raw_storage_iterator 定义于 <memory> 头文件中(C++17 起被标记为 deprecated,但理解其设计思想对掌握底层内存管理仍至关重要)。它本质是一个输出迭代器适配器,将对 *iter = value 的赋值操作,转换为对目标内存地址调用 std::construct_at(或等效的 placement-new),从而绕过默认构造与赋值的开销,直接完成对象构造。
核心语义与使用约束
raw_storage_iterator 的模板参数包括:目标指针类型 OutputIterator 和元素类型 T。它仅支持前向移动(++)和解引用赋值(*it = value),不支持读取、比较或随机访问。其生命周期内,所指向的内存必须保持有效且未构造;若重复写入同一位置,将导致未定义行为(因对象未析构即重建)。
值得注意的是:该迭代器不负责内存分配,也不调用析构函数。使用者需确保后续显式调用 std::destroy 或手动析构,否则将引发资源泄漏或双重析构风险。
基本用法示例
以下代码演示如何配合 operator new 分配的原始内存,使用 raw_storage_iterator 构造对象:
#include <memory>
#include <new>
#include <iostream>
struct Widget {
int id;
Widget(int i) : id(i) { std::cout << "Widget(" << id << ") constructed\n"; }
~Widget() { std::cout << "Widget(" << id << ") destroyed\n"; }
};
int main() {
// 分配足够容纳 3 个 Widget 的原始内存(未构造)
void* raw_mem = ::operator new(3 * sizeof(Widget));
// 创建 raw_storage_iterator,指向起始地址
auto iter = std::raw_storage_iterator<Widget*, Widget>(static_cast<Widget*>(raw_mem));
// 逐个构造对象:*iter = value 触发 placement-new
*iter = Widget{101};
++iter;
*iter = Widget{102};
++iter;
*iter = Widget{103};
// 此时内存中已存在 3 个活跃对象
// 注意:不能通过 iter 读取值,也不能比较 iter 与其它迭代器
// 手动析构所有对象(顺序需与构造相反,此处为线性)
Widget* ptr = static_cast<Widget*>(raw_mem);
std::destroy(ptr, ptr + 3);
// 释放原始内存
::operator delete(raw_mem);
}
运行将输出三次构造日志,且无析构日志(除非 destroy 显式调用)。若省略 std::destroy,Widget 的析构函数永远不会执行。
与 std::uninitialized_copy 等算法的协同
raw_storage_iterator 常与 std::uninitialized_copy、std::uninitialized_fill 等未初始化内存算法配合使用。例如,将已有容器内容批量复制到原始内存:
#include <vector>
#include <memory>
#include <algorithm>
int main() {
std::vector<int> src = {42, 1984, -7};
// 分配原始内存
void* raw = ::operator new(src.size() * sizeof(int));
int* dst = static_cast<int*>(raw);
// 使用 raw_storage_iterator 封装目标
auto out_iter = std::raw_storage_iterator<int*, int>(dst);
// 算法内部通过 *out_iter = elem 完成就地构造
std::uninitialized_copy(src.begin(), src.end(), out_iter);
// 验证结果(注意:int 是平凡类型,无需显式析构)
for (size_t i = 0; i < src.size(); ++i) {
std::cout << dst[i] << " ";
}
std::cout << "\n";
::operator delete(raw);
}
此处 std::uninitialized_copy 接收 raw_storage_iterator 后,每拷贝一个元素即触发一次 int 的就地构造(对平凡类型等价于 memcpy)。
替代方案与现代演进
C++17 引入 std::construct_at 和 std::destroy_at,使原始内存操作更直观:
#include <memory>
void modern_way() {
void* raw = ::operator new(sizeof(std::string));
std::string* p = static_cast<std::string*>(raw);
// 替代 *iter = value
std::construct_at(p, "hello");
// 替代 std::destroy
std::destroy_at(p);
::operator delete(raw);
}
同时,std::allocator_traits::construct 提供了更通用的分配器感知构造方式。正因这些更安全、更明确的接口出现,raw_storage_iterator 在 C++17 中被弃用,并于 C++20 中移除。
总结
raw_storage_iterator 是 C++ 内存抽象演进中的一个重要过渡组件。它揭示了标准库如何将“赋值语义”重定向为“构造语义”,为理解 std::vector 内部扩容、std::deque 分段存储等实现提供了底层视角。尽管其接口已被更清晰的工具取代,但掌握其原理有助于编写高效、可控的内存敏感型代码,并避免在自定义容器中重复发明轮子。
在实际工程中,应优先选用 std::construct_at、std::uninitialized_* 系列算法及 std::allocator 接口;仅在维护遗留代码或深入研究 STL 实现时,才需直接操作 raw_storage_iterator。对内存生命周期的敬畏,始终是 C++ 程序员的核心素养。

