使用std::atomic实现引用计数可确保多线程下对象安全共享,通过原子操作避免竞态条件。其核心是利用std::atomic<long>作为引用计数器,在增减时保证操作不可分割,防止内存泄漏或双重释放。增加引用时用memory_order_relaxed提升性能,减少引用并判断是否释放资源时用memory_order_acq_rel确保内存顺序与可见性。自定义实现需注意循环引用、异常安全、自我赋值等问题,最佳实践是优先使用std::shared_ptr,若自研则遵循RAII、分离控制块并充分测试。

在C++中,使用
std::atomic
std::atomic
要使用
std::atomic
std::shared_ptr
std::atomic<long>
std::atomic<int>
我们先定义一个管理类,它内部持有实际的数据和引用计数。
#include <atomic>
#include <iostream>
#include <utility> // For std::exchange
template<typename T>
class AtomicSharedPtr {
private:
T* data_ptr;
std::atomic<long>* ref_count; // 使用原子类型管理引用计数
void decrement_and_check() {
if (ref_count && ref_count->fetch_sub(1, std::memory_order_acq_rel) == 1) {
// 当引用计数减到1(即即将变为0)时,说明当前是最后一个引用
delete data_ptr;
delete ref_count;
data_ptr = nullptr;
ref_count = nullptr;
}
}
public:
// 默认构造函数
AtomicSharedPtr() : data_ptr(nullptr), ref_count(nullptr) {}
// 接受裸指针的构造函数
explicit AtomicSharedPtr(T* ptr) : data_ptr(ptr) {
if (data_ptr) {
ref_count = new std::atomic<long>(1); // 初始化计数为1
} else {
ref_count = nullptr;
}
}
// 拷贝构造函数
AtomicSharedPtr(const AtomicSharedPtr& other) noexcept :
data_ptr(other.data_ptr), ref_count(other.ref_count) {
if (ref_count) {
ref_count->fetch_add(1, std::memory_order_relaxed); // 增加引用计数
}
}
// 移动构造函数
AtomicSharedPtr(AtomicSharedPtr&& other) noexcept :
data_ptr(std::exchange(other.data_ptr, nullptr)),
ref_count(std::exchange(other.ref_count, nullptr)) {}
// 拷贝赋值运算符
AtomicSharedPtr& operator=(const AtomicSharedPtr& other) noexcept {
if (this != &other) { // 避免自我赋值
decrement_and_check(); // 先释放当前持有的资源
data_ptr = other.data_ptr;
ref_count = other.ref_count;
if (ref_count) {
ref_count->fetch_add(1, std::memory_order_relaxed); // 增加新资源的引用计数
}
}
return *this;
}
// 移动赋值运算符
AtomicSharedPtr& operator=(AtomicSharedPtr&& other) noexcept {
if (this != &other) { // 避免自我赋值
decrement_and_check(); // 释放当前资源
data_ptr = std::exchange(other.data_ptr, nullptr);
ref_count = std::exchange(other.ref_count, nullptr);
}
return *this;
}
// 析构函数
~AtomicSharedPtr() {
decrement_and_check();
}
// 提供访问底层数据的方法
T* get() const noexcept { return data_ptr; }
T& operator*() const noexcept { return *data_ptr; }
T* operator->() const noexcept { return data_ptr; }
// 获取当前引用计数(仅供调试或特殊场景,非线程安全读取)
long use_count() const noexcept {
return ref_count ? ref_count->load(std::memory_order_relaxed) : 0;
}
// 检查是否为空
explicit operator bool() const noexcept {
return data_ptr != nullptr;
}
};
// 示例用法
struct MyData {
int value;
MyData(int v) : value(v) { std::cout << "MyData(" << value << ") constructed.\n"; }
~MyData() { std::cout << "MyData(" << value << ") destructed.\n"; }
};
int main() {
AtomicSharedPtr<MyData> ptr1(new MyData(100));
std::cout << "ptr1 use_count: " << ptr1.use_count() << "\n"; // 1
{
AtomicSharedPtr<MyData> ptr2 = ptr1; // 拷贝构造
std::cout << "ptr1 use_count: " << ptr1.use_count() << "\n"; // 2
std::cout << "ptr2 use_count: " << ptr2.use_count() << "\n"; // 2
AtomicSharedPtr<MyData> ptr3(new MyData(200));
ptr3 = ptr1; // 拷贝赋值
std::cout << "ptr1 use_count: " << ptr1.use_count() << "\n"; // 3
std::cout << "ptr3 use_count: " << ptr3.use_count() << "\n"; // 3
// ptr2, ptr3 离开作用域,引用计数减少
} // ptr2, ptr3 析构,计数变为1
std::cout << "ptr1 use_count after scope: " << ptr1.use_count() << "\n"; // 1
AtomicSharedPtr<MyData> ptr4;
ptr4 = std::move(ptr1); // 移动赋值
std::cout << "ptr4 use_count: " << ptr4.use_count() << "\n"; // 1
std::cout << "ptr1 is now empty: " << (ptr1 ? "false" : "true") << "\n"; // true
// ptr4 离开作用域,MyData(100) 被析构
return 0;
}这里我们用了
fetch_add
fetch_sub
std::atomic
fetch_sub
delete data_ptr; delete ref_count;
立即学习“C++免费学习笔记(深入)”;
int
这个问题,其实是多线程编程里一个很经典也很容易被忽视的陷阱。想象一下,如果我们在多线程环境中使用一个普通的
int
int ref_count;
ref_count++;
ref_count--;
++
ref_count
ref_count
设想一下,两个线程A和B同时尝试增加一个初始值为1的计数器:
ref_count
ref_count
ref_count
ref_count
ref_count
ref_count
结果是,我们期望的
ref_count
std::atomic
LOCK CMPXCHG
std::atomic
内存序(Memory Order)是
std::atomic
对于引用计数,我们主要关注两种操作:增加引用计数和减少引用计数。
增加引用计数(fetch_add
AtomicSharedPtr
std::memory_order_relaxed
relaxed
ref_count->fetch_add(1, std::memory_order_relaxed);
减少引用计数(fetch_sub
std::memory_order_acq_rel
fetch_sub(1, std::memory_order_acq_rel)
fetch_sub
acquire
fetch_sub
release
简单来说,
acq_rel
if (ref_count->fetch_sub(1, std::memory_order_acq_rel) == 1) {
// 此时,确保了所有之前对data_ptr的写入都已完成并可见
delete data_ptr;
delete ref_count;
}虽然
std::shared_ptr
relaxed
acq_rel
seq_cst
自定义引用计数类,尤其是像
std::shared_ptr
常见陷阱:
AtomicSharedPtr<B>
AtomicSharedPtr<A>
std::shared_ptr
std::weak_ptr
weak_ptr
new MyData(args)
new std::atomic<long>(1)
MyData
try-catch
AtomicSharedPtr
AtomicSharedPtr
if (this != &other)
other
delete data_ptr
data_ptr
ref_count
nullptr
decrement_and_check
最佳实践:
std::shared_ptr
std::weak_ptr
AtomicSharedPtr
std::shared_ptr
AtomicSharedPtr
std::atomic
std::memory_order_relaxed
std::memory_order_acq_rel
以上就是C++如何使用std::atomic实现引用计数的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号