You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为带内部引用计数的不透明结构体实现可拷贝可移动包装器

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:21:17