C++ 如何将自定义删除程序与std::unique_ptr成员一起使用?
我有一个拥有唯一成员的类C++ 如何将自定义删除程序与std::unique_ptr成员一起使用?,c++,c++11,move-semantics,unique-ptr,C++,C++11,Move Semantics,Unique Ptr,我有一个拥有唯一成员的类 class Foo { private: std::unique_ptr<Bar> bar; ... }; 有没有一种方法可以将std::unique\u ptr作为类的成员来执行此操作?您只需将std::bind与销毁函数一起使用即可 std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy, s
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
有没有一种方法可以将
std::unique\u ptr
作为类的成员来执行此操作?您只需将std::bind
与销毁函数一起使用即可
std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy,
std::placeholders::_1));
std::unique\u ptr条(创建(),std::绑定和销毁,
std::占位符::1));
当然你也可以用lambda
std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);});
std::unique_ptr ptr(create(),[](Bar*b){destroy(b);});
您只需创建一个删除器类:
struct BarDeleter {
void operator()(Bar* b) { destroy(b); }
};
并将其作为unique\u ptr
的模板参数提供。您仍然需要在构造函数中初始化唯一的\u ptr:
class Foo {
public:
Foo() : bar(create()), ... { ... }
private:
std::unique_ptr<Bar, BarDeleter> bar;
...
};
class-Foo{
公众:
Foo():bar(create()),…{…}
私人:
std::唯一的ptr条;
...
};
据我所知,所有流行的C++库都正确地实现了这一点;由于
BarDeleter
实际上没有任何状态,因此它不需要占用unique\u ptr中的任何空间,假设create
和destroy
是具有以下签名的自由函数(从OP的代码片段来看似乎是这样的):
Bar* create();
void destroy(Bar*);
你可以这样写你的类Foo
class Foo {
std::unique_ptr<Bar, void(*)(Bar*)> ptr_;
// ...
public:
Foo() : ptr_(create(), destroy) { /* ... */ }
// ...
};
class-Foo{
std::唯一的ptr ptr;
// ...
公众:
Foo():ptr_(create(),destroy){/*…*/}
// ...
};
请注意,您不需要在此处编写任何lambda或自定义删除程序,因为destroy
已经是一个删除程序。可以使用C++11中的lambda(在G++4.8.2中测试)干净地执行此操作
给定此可重用的typedef
:
template<typename T>
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>;
通过这种方式,您可以使用RAII获得异常安全清理的好处,而无需尝试/捕获噪音。您知道,使用自定义删除程序不是最好的方法,因为您必须在代码中提到它。
相反,对于::std
中的命名空间级类,只要涉及自定义类型并且您尊重语义,请执行以下操作:
专攻:
除非您需要能够在运行时更改删除器,否则我强烈建议使用自定义删除器类型。例如,如果为删除程序使用函数指针,sizeof(unique_ptr)==2*sizeof(T*)
。换句话说,unique\u ptr
对象的一半字节被浪费了
不过,编写自定义删除程序来包装每个函数是一件麻烦事。谢天谢地,我们可以在函数上编写一个模板类型:
从C++17开始:
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
template <typename T, auto fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
// usage:
my_unique_ptr<Bar, destroy> p{create()};
模板
使用deleter_from_fn=std::integral_常量;
模板
使用my_unique_ptr=std::unique_ptr;
//用法:
my_unique_ptr p{create()};
在C++17之前:
template <typename D, D fn>
using deleter_from_fn = std::integral_constant<D, fn>;
template <typename T, typename D, D fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<D, fn>>;
// usage:
my_unique_ptr<Bar, decltype(destroy), destroy> p{create()};
模板
使用deleter_from_fn=std::integral_常量;
模板
使用my_unique_ptr=std::unique_ptr;
//用法:
my_unique_ptr p{create()};
使用lambda,您可以获得与普通std::unique\u ptr
相同的尺寸。比较大小:
plain: 8
lambda: 8
fpointer: 16
std::function: 40
这是下面的输出。(我在类的作用域之外声明了lambda。不确定是否可以在类内部对其进行作用域。)
#包括
#包括
#包括
结构条{};
无效销毁(Bar*b){}
Bar*create(){return 0;}
自动lambda_破坏者=[](条*b){destroy(b);};
福班{
std::唯一的ptr ptr;
公众:
Foo():ptr_(create(),lambda_destructor){/*…*/}
};
int main()
{
std::cout与C++11std::unique_ptr ptr_u;
此解决方案的缺点是它将每个unique_ptr
的开销加倍(它们都必须存储函数指针和指向实际数据的指针),每次都需要传递销毁函数,不能内联(由于模板不能专门指定特定函数,只能指定签名),必须通过指针调用函数(比直接调用成本更高)。和答案都通过专门化一个函子来避免所有这些开销。@ShadowRanger不是被定义为无论你是否显式地传递它,每次都默认删除和存储函数指针吗?我想这应该是答案。这是一个更漂亮的解决方案。或者有任何缺点,比如定义中有std::function
或诸如此类?@j00hi,我认为此解决方案有不必要的开销,因为std::function
。接受答案中的Lambda或自定义类可以与此解决方案不同地内联。但这种方法在您希望隔离专用模块中的所有实现时具有优势。如果std::function constructctor抛出(如果lambda太大而无法装入std::function对象,可能会发生这种情况)lambda真的需要吗?它可以是简单的deleted\u unique\u ptr foo(new foo(),customdeleter);
如果customdeleter
遵循约定(它返回void并接受原始指针作为参数)。这种方法有一个缺点。std::function不需要尽可能使用move构造函数。这意味着当您使用std::move(my_deleted_unique_ptr)时,lambda包含的内容可能会被复制而不是移动,这可能是您想要的,也可能不是您想要的。此选项是唯一适用于数组、std::vector和其他集合的选项,因为它可以使用零参数std::unique_ptr构造函数。其他答案使用无法访问此零参数构造函数的解决方案原因构造唯一指针时必须提供删除器实例。但此解决方案为std::unique_ptr
(std::unique_ptr
)提供了一个删除器类(struct BarDeleter
)它允许std::unique\u ptr
构造函数自己创建一个Deleter实例。也就是说,下面的代码是允许的std::unique\u ptr条[10];
我将创建一个typedef,以便于使用typedef std::UniqueBarPtr
@DavidF:Or使用,这具有相同的优点(内联删除,每个唯一\u ptr
上没有额外存储,无需提供dele实例
template <>
struct ::std::default_delete<Bar> {
default_delete() = default;
template <class U>
constexpr default_delete(default_delete<U>) noexcept {}
void operator()(Bar* p) const noexcept { destroy(p); }
};
template <>
inline ::std::unique_ptr<Bar> ::std::make_unique<Bar>() {
auto p = create();
if (!p)
throw std::runtime_error("Could not `create()` a new `Bar`.");
return { p };
}
template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;
template <typename T, auto fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
// usage:
my_unique_ptr<Bar, destroy> p{create()};
template <typename D, D fn>
using deleter_from_fn = std::integral_constant<D, fn>;
template <typename T, typename D, D fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<D, fn>>;
// usage:
my_unique_ptr<Bar, decltype(destroy), destroy> p{create()};
plain: 8
lambda: 8
fpointer: 16
std::function: 40
#include <iostream>
#include <memory>
#include <functional>
struct Bar {};
void destroy(Bar* b) {}
Bar* create() { return 0; }
auto lambda_destroyer = [](Bar* b) {destroy(b);};
class Foo {
std::unique_ptr<Bar, decltype(lambda_destroyer)> ptr_;
public:
Foo() : ptr_(create(), lambda_destroyer) { /* ... */ }
};
int main()
{
std::cout << "plain: " << sizeof (std::unique_ptr<Bar>) << std::endl
<< "lambda: " << sizeof (std::unique_ptr<Bar, decltype(lambda_destroyer)>) << std::endl
<< "fpointer: " << sizeof (std::unique_ptr<Bar, void(*)(Bar*)>) << std::endl
<< "std::function: " << sizeof (std::unique_ptr<Bar, std::function<void(Bar*)>>) << std::endl;
}