C++ 如何使用带有 std::unique_ptr 成员的自定义删除器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19053351/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:27:28  来源:igfitidea点击:

How do I use a custom deleter with a std::unique_ptr member?

c++c++11move-semanticsunique-ptr

提问by huitlarc

I have a class with a unique_ptr member.

我有一个带有 unique_ptr 成员的类。

class Foo {
private:
    std::unique_ptr<Bar> bar;
    ...
};

The Bar is a third party class that has a create() function and a destroy() function.

Bar 是具有 create() 函数和 destroy() 函数的第三方类。

If I wanted to use a std::unique_ptrwith it in a stand alone function I could do:

如果我想std::unique_ptr在一个独立的函数中使用它,我可以这样做:

void foo() {
    std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
    ...
}

Is there a way to do this with std::unique_ptras a member of a class?

有没有办法以std::unique_ptr班级成员的身份做到这一点?

回答by Cassio Neri

Assuming that createand destroyare free functions (which seems to be the case from the OP's code snippet) with the following signatures:

假设createdestroy是具有以下签名的免费功能(OP 的代码片段似乎就是这种情况):

Bar* create();
void destroy(Bar*);

You can write your class Foolike this

你可以写你的类Foo像这样

class Foo {

    std::unique_ptr<Bar, void(*)(Bar*)> ptr_;

    // ...

public:

    Foo() : ptr_(create(), destroy) { /* ... */ }

    // ...
};

Notice that you don't need to write any lambda or custom deleter here because destroyis already a deleter.

请注意,您无需在此处编写任何 lambda 或自定义删除器,因为destroy它已经是一个删除器。

回答by Drew Noakes

It's possible to do this cleanly using a lambda in C++11 (tested in G++ 4.8.2).

可以使用 C++11 中的 lambda 干净利落地做到这一点(在 G++ 4.8.2 中测试)。

Given this reusable typedef:

鉴于此可重用typedef

template<typename T>
using deleted_unique_ptr = std::unique_ptr<T,std::function<void(T*)>>;

You can write:

你可以写:

deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); });


For example, with a FILE*:

例如,使用FILE*

deleted_unique_ptr<FILE> file(
    fopen("file.txt", "r"),
    [](FILE* f) { fclose(f); });

With this you get the benefits of exception-safe cleanup using RAII, without needing try/catch noise.

有了这个,您可以获得使用 RAII 进行异常安全清理的好处,而无需 try/catch 噪音。

回答by rici

You just need to create a deleter class:

你只需要创建一个删除器类:

struct BarDeleter {
  void operator()(Bar* b) { destroy(b); }
};

and provide it as the template argument of unique_ptr. You'll still have to initialize the unique_ptr in your constructors:

并将其作为 的模板参数提供unique_ptr。您仍然需要在构造函数中初始化 unique_ptr :

class Foo {
  public:
    Foo() : bar(create()), ... { ... }

  private:
    std::unique_ptr<Bar, BarDeleter> bar;
    ...
};

As far as I know, all the popular c++ libraries implement this correctly; since BarDeleterdoesn't actually have any state, it does not need to occupy any space in the unique_ptr.

据我所知,所有流行的 C++ 库都正确地实现了这一点;由于BarDeleter实际上没有任何状态,因此不需要在unique_ptr.

回答by Justin

Unless you need to be able to change the deleter at runtime, I would strongly recommend using a custom deleter type. For example, if use a function pointer for your deleter, sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*). In other words, half of the bytes of the unique_ptrobject are wasted.

除非您需要能够在运行时更改删除器,否则我强烈建议使用自定义删除器类型。例如,如果为您的删除器使用函数指针,sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*). 换句话说,unique_ptr对象的一半字节被浪费了。

Writing a custom deleter to wrap every function is a bother, though. Thankfully, we can write a type templated on the function:

不过,编写一个自定义删除器来包装每个函数是一件麻烦事。值得庆幸的是,我们可以在函数上编写一个模板类型:

Since C++17:

从 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()};

Prior to C++17:

在 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()};

回答by mkaes

You can simply use std::bindwith a your destroy function.

您可以简单地std::bind与您的销毁功能一起使用。

std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy,
    std::placeholders::_1));

But of course you can also use a lambda.

当然,您也可以使用 lambda。

std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);});

回答by Deduplicator

You know, using a custom deleter isn't the best way to go, as you will have to mention it all over your code.
Instead, as you are allowed to add specializationsto namespace-level classes in ::stdas long as custom types are involved and you respect the semantics, do that:

您知道,使用自定义删除器并不是最好的方法,因为您必须在整个代码中提及它。
相反,只要涉及自定义类型并且尊重语义,您就可以向命名空间级别的类添加特::std化,请执行以下操作:

Specialize std::default_delete:

专精std::default_delete

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); }
};

And maybe also do std::make_unique():

也许还可以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 };
}