2

我有一个类似于How to manage object life time using Boost library smart pointers 的问题?但是,就我而言,“对象”根本不是 C++ 对象,而是从 C API 返回/传递的不透明类型。该类型没有指针语义,即没有解引用;但是,它作为参数传递给 C API 中的其他函数。该类型还有一个明确的closeAPI,必须调用它才能清理内部资源。

所以,我有一个类似的 C API

opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'

由于各种原因,在我的 C++ 代码中,我想管理 opaque_legacy_type_t 的“实例”,就像管理堆分配的对象实例一样,即具有与boost::shared_ptr<>. 似乎提供了足够的东西,我可以通过这样做shared_ptr来管理调用:XXclose

opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);

但是,由于opaque_legacy_type_t没有指针语义,所以使用managed有点笨拙。

我想做的是有类似于 a 的东西managed_typeshared_ptr并且正在寻找不需要我全部写出来的想法。

编辑: 我在示例中纠正了我原来的错误。遗留 API 通过值而不是指针来获取不透明类型。

4

4 回答 4

2

由于所有遗留 API 都采用指向 opaque 类型的指针,因此您可以直接使用共享指针。关键是您不要在堆栈上声明原始结构,而是通过以下方式分配它new

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(x.get(), ...);
}


编辑:如果某些 API 按值而不是指针采用不透明类型,则传递取消引用的指针。

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(*p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(*x, ...);
}
于 2012-02-15T20:29:59.903 回答
1

您可以编写一个与 boost 一起使用的包装器,它将调用open()ctor 和close()dtor 中的 。

于 2012-02-15T20:15:39.020 回答
1

您可以将 boost 智能指针与 pimpl idom 一起使用:

class shared_opaque_legacy_type_t {
    struct impl {
        opaque_legacy_type_t t;
        impl(...) { XXOpen(..., t); }
        ~impl(...) { XXClose(t); }
    }
    boost::shared_ptr<impl> _impl;
public:
    shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}

    opaque_legacy_type_t* get() {
        return _impl->t;
    }
};


shared_opaque_legacy_type_t x(...);
XXdoSomethingWithResource(x.get(), ...);

缺点是您仍然可以调用XXclose(x.get())并使您的对象无效。

更新:修复它。:-)

于 2012-02-15T20:26:31.660 回答
0

我投票支持 Rob 的答案,它只使用 ashared_ptr而没有包装器,但如果你真的想避免动态分配,这里有一个简单的小例子来说明如何做到这一点。

它是一个模板,直接持有句柄,不进行分配。您向构造函数传递一个创建不透明类型对象的函子,以及一个在需要销毁该类型时调用的删除器。它是可移动且不可复制的,因此现在需要共享引用计数。它实现了隐式转换运算符,因此您可以在使用所持有类型的值的地方使用它。

template<typename T,typename D>
class opaque_type_handle {
    T handle;
    D deleter;
    bool needs_delete;
public:
    template<typename F>
    opaque_type_handle(F f,D d) : handle(f()), deleter(d), needs_delete(true) {}

    opaque_type_handle(opaque_type_handle const &) = delete;
    opaque_type_handle &operator=(opaque_type_handle const &) = delete;

    opaque_type_handle(opaque_type_handle &&rhs) : handle(rhs.handle),deleter(rhs.deleter),needs_delete(true) {
        rhs.needs_delete = false;
    }
    opaque_type_handle &operator=(opaque_type_handle &&rhs) {
        handle = rhs.handle;
        deleter = rhs.deleter;
        needs_delete = true;
        rhs.needs_delete = false;
        returh *this;
    }

    ~opaque_type_handle() {
        if(needs_delete) {
            deleter(handle);
        }
    }

    operator T&() { return handle; }
    operator T() const { return handle; }
};

像这样使用它:

// wrap up the code for creating an opaque_legacy_type_t handle
typedef opaque_type_handle<opaque_legacy_type_t,decltype(&XXclose)> legacy_handle;

legacy_handle make_legacy_handle(...) {
    return legacy_handle(
        [](){
            opaque_legacy_type_t tmp;
            XXopen(..., &tmp);
            return tmp;
        },
        &XXclose
    );
}

legacy_handle x = make_legacy_handle(...);
XXdoSomethingWithResource(x,...);
于 2012-02-16T16:50:02.423 回答