句柄而不是指针的引用计数

Posted

技术标签:

【中文标题】句柄而不是指针的引用计数【英文标题】:Reference count for a handle instead of a pointer 【发布时间】:2015-10-12 19:50:31 【问题描述】:

C++11 引入了像std::shared_ptr 这样的智能指针。该类存储一个指针和一个引用计数器。当引用计数器达到零时,将调用回调(删除器)。我的问题是 C++11 是否有一种简单的方法可以在没有指针的情况下使用 std::shared_ptr 的引用计数器。

我使用了一个 c 风格的库,它给了我一个整数作为句柄。我想创建一个类来包装句柄。我想避免与std::shared_ptr 的间接关系,而我想在不再需要时使用某种类型的引用计数来关闭句柄。如果您可以分别使用createHandledestroyHandle 创建和销毁句柄,我认为它可能如下所示:

class WrapperClass

public:
    WrapperClass() :
        mHandle(createHandle(), &destroyHandle)
    
    
private:
    shared_data<int> mHandle;

class WrapperClass

public:
    WrapperClass() :
        mHandle(createHandle()),
        mRefCount([this]()destroyHandle(mHandle);)
    
    
private:
    int mHandle;
    reference_counter mRefCount;


另一个问题是我不确定是否可以使用像const 这样的工作说明符。我的意思是,如果不使用强制转换,就不可能删除 const-specifier。我看不出有什么办法。

【问题讨论】:

我想知道你是否可以强力支持 shared_ptr 这样做。您可能会为(可能是 int)句柄编写一个简单的包装类,只是为了拥有一个自定义类型,然后为该类定义您自己的自定义运算符 free() ,它要么什么都不做,要么可能处置句柄,但什么都没有与免费商店有关。为此编写自己的 shared_resource 类可能更明智;但魔鬼可能在细节上完全正确。 如果您向自己保证永远不会将整数值存储在int 中,那么您可以使用隐式转换运算符制作一个简单的包装器到int。承诺可以防止您在没有引用计数的情况下取出值,同时仍然允许您在库需要int 描述符的任何地方将其用作函数参数。包装类将类似于 RAII 类。 @MicroVirus 而不是 promises 更好地为所有 C API 函数编写 warpper 类函数 其他可能性:句柄是某个数据结构中的索引或键,当计数为零时,您从数据结构中删除该对象。不过,您必须自己编写。 这是我以前见过的骗子 【参考方案1】:

采取一些预防措施,您可以尝试使用原始 shared_ptr 来完成此任务:

#include <iostream>
#include <memory>

int create_handle() 
    std::cout << "alloc\n";
    return 42;


void delete_handle(int handle)

    std::cout << "delete " << handle << "\n";



class Wrapper

public:
    Wrapper():
        _d(Wrapper::create_handle(), &Wrapper::delete_handle)
    

    int value()
    
        return reinterpret_cast<uintptr_t>(_d.get());
    

private:
    std::shared_ptr<void> _d;

    static void* create_handle()
    
        static_assert(sizeof(create_handle()) <= sizeof(void*), "can't fit");
        static_assert(alignof(create_handle()) <= sizeof(void*), "can't align");
        return reinterpret_cast<void*>(static_cast<uintptr_t>(::create_handle()));
    

    static void delete_handle(void* handle)
    
        return ::delete_handle(reinterpret_cast<unintptr_t>(handle));
    
;

int main()

    Wrapper w;
    std :: cout << w.value();

我相信您必须确保您的句柄可以表示为指针(匹配大小和对齐)。然后你可以申请reinterpret_cast黑魔法。因为您基本上只是将int 转换为指针并使用重新解释转换返回,但从不取消引用指针,它应该是安全的

【讨论】:

按值而不是按引用进行转换,即使这样也不能保证有效,尽管它可能在许多系统上都有效。 @NeilKirk,你的意思是 void* -> int 转换吗?它甚至不适用于我的 Ubuntu。你的理由是什么? reinterpret_cast&lt;uintptr_t&gt;(ptr) 是将指针转换为整数的正确方法。 @NeilKirk,所以,像这样? 假设 uintptr_t 足够大以容纳所有 int 值,是的。您可以使用std::numeric_limits 进行检查。

以上是关于句柄而不是指针的引用计数的主要内容,如果未能解决你的问题,请参考以下文章

您对 C、C++ 和 Java 中的指针、引用和句柄有啥深刻的理解?

指针和引用与及指针常量和常量指针

一文掌握 C++ 智能指针的使用

JVM笔记5-对象的访问定位。

指针的指针,指针的引用(不是二维指针)

引用和指针的区别