我想拥有一个提供一些创建方法的运行时界面。这些方法返回unique_ptr<T>
,我想通过创建类启用自定义删除。问题是我绝对不希望接口直接提供这些方法 - 它们只能在unique_ptr<T, SomeCustomDel>
的销毁中使用。现在,我认为我可以使用std::unique_ptr<T, std::function<void(T*)>>
,但我真的不愿意,因为我根本不需要这种抽象级别,我不想支付堆分配。
有什么建议吗?
答案 0 :(得分:2)
您的说明书对我来说并不完全清楚,但您是否考虑过unique_ptr<T, void(*)(void*)>
?这是一种非常灵活的类型,具有许多动态删除器的特性。
如果这不是您想要的,您可以尝试以下方式:
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();}
...
};
如果没有关于您的要求的更多详细信息,很难知道您的情况最好。
答案 1 :(得分:2)
我希望std::unique_ptr
有一个标准的“动态”删除版本。这个神秘的类允许我在实例化时将unique_ptr附加到unique_ptr,类似于std::shared_ptr
。
如果说这种类型存在,我怀疑它基本上是用std::unique_ptr<T,std::function<void(T*)>>
来实现的。你想避免的事情。
但是我认为你低估了std::function
。它的实现是优化,以避免在可能的情况下击中堆。如果你的删除对象仍然很小,一切都将在堆栈上完成(我认为boost::function
可以静态处理最大32字节的删除。)
对于过于笼统的删除问题。您必须提供删除器的定义。没有办法解决这个问题。但是,您不必让用户实例化该类,这实际上禁止他们使用它。为此,删除器的构造函数需要一个仅在实现文件中定义的标记结构。
或者可能是最简单的解决方案。将删除器放在详细命名空间中。用户仍然可以自由使用它,但很明显,当你更改它时,他们不应该也不会抱怨,打破他们的代码。
答案 2 :(得分:1)
我看到两个选项。
选项1:如果需要,使用包含函数指针和可选的原始char数组的自定义删除器来编码某些状态:
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*)>>;
现在,您可以创建不同的upi<T>
对象,这些对象存储不同的函数指针和删除状态,而无需提及其类型中究竟发生了什么。但这几乎与实现“小功能优化”的function<>
删除器相同。您可以期待一个体面的标准库实现,为不需要任何堆分配的小函数对象(如函数指针)提供非常有效的function<>
包装器。至少我这样做。 :)
选项2:只需使用shared_ptr而不是unique_ptr,并对删除器使用其内置类型擦除功能。这也可以让您轻松支持Derived-&gt; Base转换。为了最好地控制可以使用std :: allocate_shared函数模板分配的内容。
答案 3 :(得分:1)
这是对其中一个答案的回答,而不是原始问题。仅仅因为格式化原因,这是答案而不是评论。
我希望有一个标准的“动态” 删除版
std::unique_ptr
。 这个神话般的课程允许我这样做 将删除者附加到unique_ptr
当我实例化它时,类似于std::shared_ptr
。
以下是此类实现的开始。这很容易做到。我仅将unique_ptr
用作异常安全辅助工具,仅此而已。它并不像你想的那样功能齐全。这些额外的功能留给读者练习。 :-)以下内容为自定义动态删除器建立指针和存储的唯一所有权。请注意,即使智能指针的构造函数抛出,智能指针也拥有传入的指针(这实际上是unique_ptr
在实现中最有用的地方)。
#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_;}
};
请注意,与unique_ptr
(和shared_ptr
}不同,采用指针的构造函数不是noexcept
。虽然可以通过使用“小删除器”优化来减轻这种情况。另一个练习留给读者。 : - )
答案 4 :(得分:0)
我发现这个问题在谷歌上搜索我自己的问题;将unique_ptr与抽象基类指针一起使用。所有答案都很棒。我发现@ deft_code是最符合我需求的。
这就是我最终在我的案例中做的事情:
假设T是抽象基类类型。 makeT(),func创建一些派生类的新实例,并返回T *:
std::unique_ptr<T, std::function<void(T*)>> p(makeT(), [](T* p){p.delete();});
我只想分享那些正在寻找简短,复制和粘贴解决方案的人。
对于未经训练的c ++ 11眼睛,[](...语法是lambda。
正如其他答案中所提到的,它不是C语义中的'函数指针',而是一个可调用的c ++对象,它实际上很小,并且应该具有可忽略的开销来承载它。