C++ 将抽象删除器与std::unique\u ptr一起使用

C++ 将抽象删除器与std::unique\u ptr一起使用,c++,c++11,visual-studio-2010,C++,C++11,Visual Studio 2010,我希望有一个运行时接口,提供一些创建方法。这些方法返回unique\u ptr,我希望通过创建类启用自定义删除。问题是,我绝对不希望接口直接提供这些方法-它们应该只在销毁unique\u ptr时可用。现在,我想我可以使用std::unique\u ptr,但我真的不想使用,因为我不需要那种抽象级别,也不想支付堆分配 有什么建议吗?我有两个选择 选项1:如有必要,使用包含函数指针和可选原始字符数组的自定义删除程序对某些状态进行编码: template<class T> void si

我希望有一个运行时接口,提供一些创建方法。这些方法返回
unique\u ptr
,我希望通过创建类启用自定义删除。问题是,我绝对不希望接口直接提供这些方法-它们应该只在销毁
unique\u ptr
时可用。现在,我想我可以使用
std::unique\u ptr
,但我真的不想使用,因为我不需要那种抽象级别,也不想支付堆分配

有什么建议吗?

我有两个选择

选项1:如有必要,使用包含函数指针和可选原始字符数组的自定义删除程序对某些状态进行编码:

template<class T>
void simply_delete(T* ptr, const unsigned char*) {
    delete ptr;
}

template<class T, int StateSize>
struct my_deleter {
    void (*funptr)(T*,const unsigned char*);
    array<unsigned char,StateSize> state;

    my_deleter() : funptr(&simply_delete<T>) {}

    void operator()(T* ptr) const {
        funptr(ptr,StateSize>0 ? &state[0] : nullptr);
    }
};

template<class T>
using upi = unique_ptr<T,my_deleter<T,sizeof(void*)>>;
模板
void simply_delete(T*ptr,const unsigned char*){
删除ptr;
}
模板
结构我的删除器{
void(*funptr)(T*,常量无符号字符*);
阵列状态;
my_deleter():funptr(&simply_delete){}
void运算符()(T*ptr)常量{
funptr(ptr,StateSize>0?&状态[0]:nullptr);
}
};
模板
使用upi=唯一的ptr;
现在,您可以创建不同的
upi
对象来存储不同的函数指针和删除器状态,而无需提及其类型中到底发生了什么。但这几乎与实现“小函数优化”的
函数
删除器相同。您可以期望一个体面的标准库实现为不需要任何堆分配的小型函数对象(如函数指针)提供一个非常高效的
函数
包装器。至少我知道。:)


选项2:只需使用shared_ptr而不是unique_ptr,并利用其内置的删除器类型擦除功能。这还允许您轻松支持派生->基转换。为了最大限度地控制在可以使用std::allocate_共享函数模板的地方分配的内容。

我不完全清楚您的规范,但您是否考虑过
唯一的\u ptr
?这是一种非常灵活的类型,具有动态删除器的许多特性

如果这不是您想要的,您可以尝试以下方法:

class impl
{
public:
    virtual ~impl();

    virtual void operator()(void*) = 0;
    virtual void other_functionality() = 0;
};

class my_deleter
{
    impl* p_;
public:
    ...
    void operator()(void* p) {(*p_)(p);}
    void other_functionality() {p_->other_functionality();}
    ...
};

如果没有关于您的需求的更多细节,就很难知道什么是最好的。

我希望有一个标准的
std::unique\u ptr的“动态”删除版本。这个虚构的类允许我在实例化唯一的\u ptr时将deleter附加到它,类似于
std::shared\u ptr

也就是说,如果存在这样一种类型,我怀疑它本质上是通过
std::unique\u ptr
实现的。这正是你想要避免的

然而,我认为您低估了
std::function
。它的实现是优化,以尽可能避免命中堆。如果您的deleter对象仍然很小,那么所有操作都将在堆栈上完成(我认为
boost::function
可以静态处理多达32字节的deleter)

A用于解决太一般的删除器问题。您必须提供删除器的定义。这是没有办法的。但是,您不必让用户实例化该类,这实际上禁止用户使用它。为此,删除程序的构造函数需要只在实现文件中定义的标记结构


或者可能是最简单的解决方案。将删除程序放在详细名称空间中。用户仍然可以自由使用它,但很明显,当您更改它时,他们不应该也不能抱怨,因为您破坏了他们的代码。

这是对其中一个答案的回答,而不是对原始问题的回答。这是一个答案,而不是一个简单的评论,因为格式的原因


我希望有一个标准的“动态” 删除版本的
std::unique\u ptr
。 这门神秘的课程会让我 将删除程序附加到
唯一\u ptr
当我实例化它时,类似于
std::shared\u ptr

下面是此类实现的开始。这相当容易做到。我只使用了
unique\u ptr
作为异常安全辅助,仅此而已。它并不像您希望的那样功能齐全。这些额外功能留给读者作为练习。:-)以下内容为自定义动态删除器建立指针和存储的唯一所有权。请注意,即使智能指针的构造函数抛出,智能指针也拥有传入的指针(这实际上是
unique\u ptr
在实现中最有用的地方)

#包括
#包括
名称空间详细信息
{
类impl
{
公众:
虚拟~impl(){};
};
模板
类擦除类型
:公共impl
{
T*T_;
D_;
公众:
显式擦除类型(T*T)
noexcept(std::is_nothrow_default_constructible::value)
:t(t)
{}
擦除类型(T*T,常数D&D)
noexcept(std::is\u nothrow\u copy\u constructible::value)
:t_ut(t),
d_ud(d)
{}
擦除类型(T*T、D和D)
noexcept(std::is\u nothrow\u move\u constructible::value)
:t_ut(t),
(标准:移动(d))
{}
虚拟~erase_type()
{
如果(t_)
d_(t_);
}
擦除类型(常量擦除类型&)=删除;
擦除类型和运算符=(常量擦除类型和)=删除;
};
}//细节
模板
给我的指针分类
{
T*ptr;
详细信息::impl*impl\ux;
公众:
我的指针()不例外
:ptr_(空ptr),
impl_u3;(nullptr)
{}
模板
显式my_指针(Y*p)
:ptr_(静态_铸造(p)),
impl_u3;(nullptr)
{
标准:唯一的ptr保持(p);
impl_uu=新细节::擦除类型(p);
保持。释放();
}
模板
显式my_指针(Y*p,D&&D)
:ptr_(静态_铸造(p)),
impl_u3;(nullptr)
{
标准:唯一的ptr保持(p,d);
类型定义
细节::擦除类型
删除类型;
impl_=
#include <memory>
#include <type_traits>

namespace detail
{

class impl
{
public:
    virtual ~impl() {};
};

template <class T, class D>
class erase_type
    : public impl
{
    T* t_;
    D d_;

public:
    explicit erase_type(T* t)
            noexcept(std::is_nothrow_default_constructible<D>::value)
        : t_(t)
    {}

    erase_type(T* t, const D& d)
            noexcept(std::is_nothrow_copy_constructible<D>::value)
        : t_(t),
          d_(d)
       {}

    erase_type(T* t, D&& d)
            noexcept(std::is_nothrow_move_constructible<D>::value)
        : t_(t),
          d_(std::move(d))
       {}

    virtual ~erase_type()
    {
        if (t_)
            d_(t_);
    }

    erase_type(const erase_type&) = delete;
    erase_type& operator=(const erase_type&) = delete;
};

}  // detail

template <class T>
class my_pointer
{
    T* ptr_;
    detail::impl* impl_;

public:
    my_pointer() noexcept
        : ptr_(nullptr),
          impl_(nullptr)
    {}

    template <class Y>
    explicit my_pointer(Y* p)
        : ptr_(static_cast<T*>(p)),
          impl_(nullptr)
    {
        std::unique_ptr<Y> hold(p);
        impl_ = new detail::erase_type<Y, std::default_delete<Y>>(p);
        hold.release();
    }

    template <class Y, class D>
    explicit my_pointer(Y* p, D&& d)
        : ptr_(static_cast<T*>(p)),
          impl_(nullptr)
    {
        std::unique_ptr<Y, D&> hold(p, d);
        typedef
            detail::erase_type<Y, typename std::remove_reference<D>::type>
            ErasedType;
        impl_ = new ErasedType(p, std::forward<D>(d));
        hold.release();
    }

    ~my_pointer()
    {
        delete impl_;
    }

    my_pointer(my_pointer&& p) noexcept
        : ptr_(p.ptr_),
          impl_(p.impl_)
    {
        p.ptr_ = nullptr;
        p.impl_ = nullptr;
    }

    my_pointer& operator=(my_pointer&& p) noexcept
    {
        delete impl_;
        ptr_ = p.ptr_;
        impl_ = p.impl_;
        p.ptr_ = nullptr;
        p.impl_ = nullptr;
        return *this;
    }

    typename std::add_lvalue_reference<T>::type
    operator*() const noexcept
        {return *ptr_;}

    T* operator->() const noexcept
        {return ptr_;}
};
std::unique_ptr<T, std::function<void(T*)>> p(makeT(), [](T* p){p.delete();});