如何为带内部引用计数的不透明结构体实现可拷贝可移动包装器
C++包装带内部引用计数的C不透明结构体实现
给定的C API定义
struct Opaque { int data; int refcount; }; struct Opaque* opaque_new(int data) { return new Opaque { .data = data, .refcount = 1 }; } int opaque_data(struct Opaque* opaque) { return opaque->data; } struct Opaque* opaque_ref(struct Opaque* opaque) { opaque->refcount++; return opaque; } void opaque_unref(struct Opaque* opaque) { opaque->refcount--; if (!opaque->refcount) { delete opaque; } }
现有C++包装器代码
#include <algorithm> class Wrapper { public: Wrapper() : m_opaque(nullptr) {} explicit Wrapper(int data) : m_opaque(opaque_new(data)) {} Wrapper(const Wrapper&) = delete; // TODO Wrapper(Wrapper&& wrapper) : m_opaque(wrapper.m_opaque) { wrapper.m_opaque = nullptr; } Wrapper& operator=(const Wrapper&) = delete; // TODO Wrapper& operator=(Wrapper&& other) { swap(other); return *this; } ~Wrapper() { if (m_opaque) { opaque_unref(m_opaque); } } void swap(Wrapper& other) { std::swap(m_opaque, other.m_opaque); } int getData() const { if (m_opaque) { return opaque_data(m_opaque); } else { return 0; } } private: struct Opaque* m_opaque; };
实现剩余方法及优化方案
1. 拷贝构造函数实现
拷贝时需要对源对象的Opaque实例增加引用计数,保证多个Wrapper共享同一个实例时引用计数正确:
Wrapper(const Wrapper& other) : m_opaque(other.m_opaque ? opaque_ref(other.m_opaque) : nullptr) {}
2. 拷贝赋值运算符实现
推荐使用拷贝并交换(copy-and-swap) 惯用法,这种方式简洁且天然具备异常安全性:
Wrapper& operator=(Wrapper other) { swap(other); return *this; }
这里通过传值的方式自动触发拷贝构造(左值赋值场景)或移动构造(右值赋值场景),随后交换当前对象和临时对象的资源,临时对象销毁时会自动释放旧资源。
3. 现有方法的优化
- 移动构造函数:用
std::exchange简化代码,并标记为noexcept(帮助容器做优化):Wrapper(Wrapper&& wrapper) noexcept : m_opaque(std::exchange(wrapper.m_opaque, nullptr)) {} - swap函数:新增友元版本并提供全局
std::swap特化,方便在标准算法中直接使用:
类外补充特化:friend void swap(Wrapper& lhs, Wrapper& rhs) noexcept { lhs.swap(rhs); }namespace std { template<> void swap(Wrapper& lhs, Wrapper& rhs) noexcept { lhs.swap(rhs); } } - getData函数:简化空指针判断的写法,保持逻辑不变:
int getData() const { return m_opaque ? opaque_data(m_opaque) : 0; }
最终完整代码
#include <algorithm> #include <utility> // 用于std::exchange class Wrapper { public: Wrapper() : m_opaque(nullptr) {} explicit Wrapper(int data) : m_opaque(opaque_new(data)) {} Wrapper(const Wrapper& other) : m_opaque(other.m_opaque ? opaque_ref(other.m_opaque) : nullptr) {} Wrapper(Wrapper&& wrapper) noexcept : m_opaque(std::exchange(wrapper.m_opaque, nullptr)) {} Wrapper& operator=(Wrapper other) { swap(other); return *this; } ~Wrapper() { if (m_opaque) { opaque_unref(m_opaque); } } void swap(Wrapper& other) noexcept { std::swap(m_opaque, other.m_opaque); } int getData() const { return m_opaque ? opaque_data(m_opaque) : 0; } friend void swap(Wrapper& lhs, Wrapper& rhs) noexcept { lhs.swap(rhs); } private: struct Opaque* m_opaque; }; namespace std { template<> void swap(Wrapper& lhs, Wrapper& rhs) noexcept { lhs.swap(rhs); } }
内容的提问来源于stack exchange,提问作者Niklas
相关产品推荐
相关产品推荐

