hui*_*arc 122 c++ unique-ptr move-semantics c++11
我有一个带有unique_ptr成员的类.
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
Run Code Online (Sandbox Code Playgroud)
Bar是第三方类,具有create()函数和destroy()函数.
如果我想std::unique_ptr在独立功能中使用它,我可以这样做:
void foo() {
std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
...
}
Run Code Online (Sandbox Code Playgroud)
std::unique_ptr作为班级成员,有没有办法做到这一点?
Cas*_*eri 121
假设create并且destroy是自由函数(这似乎是来自OP的代码片段的情况)具有以下签名:
Bar* create();
void destroy(Bar*);
Run Code Online (Sandbox Code Playgroud)
你可以写你的类Foo像这样
class Foo {
std::unique_ptr<Bar, void(*)(Bar*)> ptr_;
// ...
public:
Foo() : ptr_(create(), destroy) { /* ... */ }
// ...
};
Run Code Online (Sandbox Code Playgroud)
请注意,您不需要在此处编写任何lambda或自定义删除器,因为destroy它已经是删除器.
Dre*_*kes 108
使用C++ 11中的lambda(在G ++ 4.8.2中测试)可以干净利落地完成这项工作.
鉴于此可重用typedef:
template<typename T>
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>;
Run Code Online (Sandbox Code Playgroud)
你可以写:
deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); });
Run Code Online (Sandbox Code Playgroud)
例如,用FILE*:
deleted_unique_ptr<FILE> file(
fopen("file.txt", "r"),
[](FILE* f) { fclose(f); });
Run Code Online (Sandbox Code Playgroud)
有了这个,您可以使用RAII获得异常安全清理的好处,而无需尝试/捕获噪音.
ric*_*ici 61
您只需要创建一个删除类:
struct BarDeleter {
void operator()(Bar* b) { destroy(b); }
};
Run Code Online (Sandbox Code Playgroud)
并将其作为模板参数提供unique_ptr.您仍然需要在构造函数中初始化unique_ptr:
class Foo {
public:
Foo() : bar(create()), ... { ... }
private:
std::unique_ptr<Bar, BarDeleter> bar;
...
};
Run Code Online (Sandbox Code Playgroud)
据我所知,所有流行的c ++库都能正确实现; 因为BarDeleter 实际上没有任何状态,所以不需要占用任何空间unique_ptr.
Jus*_*tin 15
除非您需要在运行时更改删除器,否则我强烈建议您使用自定义删除器类型.例如,如果为删除器使用函数指针,sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*).换句话说,unique_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()};
Run Code Online (Sandbox Code Playgroud)
在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()};
Run Code Online (Sandbox Code Playgroud)
#include "fmt/core.h"
#include <memory>
class example {};
void delete_example(example *)
{
fmt::print("delete_example\n");
}
using example_handle = std::unique_ptr<example, decltype([] (example * p)
{
delete_example(p);
})>;
int main()
{
example_handle handle(new example);
}
Run Code Online (Sandbox Code Playgroud)
只是我的两分钱,使用 C++20。
https://godbolt.org/z/Pe3PT49h4
您可以简单地使用std::bind您的destroy函数。
std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy,
std::placeholders::_1));
Run Code Online (Sandbox Code Playgroud)
但是当然您也可以使用lambda。
std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);});
Run Code Online (Sandbox Code Playgroud)
您知道,使用自定义删除器并不是最好的方法,因为您必须在代码中全部提及它。
相反,由于::std只要涉及到自定义类型并且您尊重语义,就可以向名称空间级别的类添加特殊化,因此请执行以下操作:
template <>
struct ::std::default_delete<Bar> {
default_delete() = default;
template <class U, class = std::enable_if_t<std::is_convertible<U*, Bar*>()>>
constexpr default_delete(default_delete<U>) noexcept {}
void operator()(Bar* p) const noexcept { destroy(p); }
};
Run Code Online (Sandbox Code Playgroud)
也许也可以std::make_unique():
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 };
}
Run Code Online (Sandbox Code Playgroud)