std::atomic仅支持具体类型指针(如char),因void无算术运算;必须显式指定内存序;fetch_add偏移为n*sizeof(T);不管理所指对象生命周期,需手动同步。

std::atomic 只支持指针类型,不能直接用 std::atomic
标准库不允许 std::atomic<void></void>,因为 void* 没有定义算术运算(比如 ptr + 1),而 std::atomic<t></t> 的 fetch_add、operator++ 等成员函数依赖 T 的大小做地址偏移。编译器会报错:error: no type named 'difference_type' in 'std::iterator_traits<void>'</void>。
解决办法是选一个具体类型指针,最常用的是 char* 或 uint8_t*:
std::atomic<char*> ptr{nullptr};
std::atomic<uint8_t*> data_ptr{nullptr};如果逻辑上确实需要“泛型字节指针”,用 char* 是惯用且安全的选择;它能参与指针算术,且 sizeof(char) == 1,偏移计算直观。
原子指针的初始化和 load/store 必须显式指定内存序
不同于 std::atomic<bool></bool> 或 std::atomic<int></int> 的默认 relaxed 内存序,std::atomic<t></t> 的 load() 和 store() 不提供无参重载——必须传入 std::memory_order 参数,否则编译失败:
立即学习“C++免费学习笔记(深入)”;
std::atomic<int*> p{nullptr};
p.store(new int(42)); // ❌ 编译错误:no matching member function
p.store(new int(42), std::memory_order_relaxed); // ✅常见选择:
-
std::memory_order_relaxed:仅保证原子性,不约束其他读写顺序(适合计数器、标志位) -
std::memory_order_acquire:用于load(),确保后续读写不被重排到该 load 之前 -
std::memory_order_release:用于store(),确保前面读写不被重排到该 store 之后 - 发布-获取配对(acquire-release)是实现无锁数据结构的基础
指针原子操作中 fetch_add / fetch_sub 的偏移单位是 T 的大小
fetch_add(n) 并不是加 n 字节,而是加 n * sizeof(T) 字节。例如:
struct Node { int a; double b; };
std::atomic<Node*> p{base_ptr};
p.fetch_add(1); // 实际地址增加 sizeof(Node),不是 +1这点容易误判,尤其当把 Node* 当作“数组首地址”做遍历时。若真要按字节偏移,得用 std::atomic<char></char>:
std::atomic<char*> buf{static_cast<char*>(malloc(1024))};
buf.fetch_add(16); // 确实 +16 字节混用不同指针类型时,务必确认偏移语义是否匹配业务逻辑。
delete 和 reset 需手动管理,std::atomic 不接管生命周期
std::atomic<t></t> 只保证指针值本身的读写是原子的,**完全不涉及所指对象的构造、析构或释放**。常见陷阱:
- 多个线程同时
load()到同一指针,各自delete→ 二次释放崩溃 -
store(nullptr)后原指针丢失,内存泄漏 - 没用 RAII(如
std::unique_ptr)配合,极易出错
安全做法是组合使用:
std::atomic<std::unique_ptr<int>*> safe_ptr{nullptr}; // ❌ 不合法:unique_ptr* 不可原子正确方式是用 std::atomic<:shared_ptr>></:shared_ptr>(C++20 起支持)或自行封装引用计数,或者严格约定:仅一个线程负责 delete,其他线程只读+比较交换(CAS)。
真正难的从来不是声明一个 std::atomic<t></t>,而是确保所有线程对指针所指内存的访问在时间与所有权上不冲突——这往往需要额外同步机制或更高级的无锁设计模式。


















