C++ std::unique_ptr 的自定义删除器如何工作?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8274451/
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-28 18:17:55  来源:igfitidea点击:

How does the custom deleter of std::unique_ptr work?

c++c++11unique-ptrdelete-operator

提问by Cheers and hth. - Alf

According to N3290, std::unique_ptraccepts a deleter argument in its constructor.

根据 N3290,std::unique_ptr在其构造函数中接受删除器参数。

However, I can't get that to work with Visual C++ 10.0 or MinGW g++ 4.4.1 in Windows, nor with g++ 4.6.1 in Ubuntu.

但是,我无法在 Windows 中使用 Visual C++ 10.0 或 MinGW g++ 4.4.1,也无法在 Ubuntu 中使用 g++ 4.6.1。

I therefore fear that my understanding of it is incomplete or wrong. I can't see the point of a deleter argument that's apparently ignored, so can anyone provide a working example?

因此,我担心我对它的理解不完整或错误。我看不出明显被忽略的删除器参数的意义,所以任何人都可以提供一个有效的例子吗?

Preferably I'd like to see also how that works for unique_ptr<Base> p = unique_ptr<Derived>( new Derived ).

最好我也想看看这对unique_ptr<Base> p = unique_ptr<Derived>( new Derived ).

Possibly with some wording from the standard to back up the example, i.e. that with whatever compiler you're using, it actually does what it's supposed to do?

可能使用标准中的一些措辞来支持该示例,即无论您使用什么编译器,它实际上都可以完成它应该做的事情?

采纳答案by Benjamin Lindley

This works for me in MSVC10

这在 MSVC10 中对我有用

int x = 5;
auto del = [](int * p) { std::cout << "Deleting x, value is : " << *p; };
std::unique_ptr<int, decltype(del)> px(&x, del);

And on gcc 4.5, here

在 gcc 4.5 上,这里

I'll skip going to the standard, unless you don't think that example is doing exactly what you'd expect it to do.

我将跳过标准,除非您认为该示例没有完全按照您的预期执行。

回答by Philippe Cayouette

To complement all previous answers, there is a way to have a custom deleter without having to "pollute" the unique_ptr signature by having either a function pointer or something equivalent in it like this:

为了补充所有以前的答案,有一种方法可以使用自定义删除器,而不必通过使用函数指针或类似这样的函数指针来“污染”unique_ptr 签名:

std::unique_ptr< MyType, myTypeDeleter > // not pretty

This is achievable by providing a specialization to the std::default_delete template class, like this:

这可以通过为 std::default_delete 模板类提供特化来实现,如下所示:

namespace std
{
template<>
class default_delete< MyType >
{
public:
  void operator()(MyType *ptr)
  {
    delete ptr;
  }
};
}

And now all std::unique_ptr< MyType >that "sees" this specialization will be deleted with it. Just be aware that it might not be what you want for all std::unique_ptr< MyType >, so chose carefully your solution.

现在,所有std::unique_ptr< MyType >“看到”该专业化的内容都将随之删除。请注意,它可能不是您想要的std::unique_ptr< MyType >,因此请谨慎选择您的解决方案。

回答by Cheers and hth. - Alf

My question has been pretty well answered already.

我的问题已经得到很好的回答。

But just in case people wondered, I had the mistaken belief that a unique_ptr<Derived>could be moved to a unique_ptr<Base>and would then remember the deleter for the Derivedobject, i.e., that Basewould not need to have a virtual destructor. That was wrong. I'd select Kerrek SB's commentas "the answer", except one cannot do that for a comment.

但以防万一人们想知道,我错误地认为 aunique_ptr<Derived>可以移动到 aunique_ptr<Base>然后会记住Derived对象的删除器,即Base不需要虚拟析构函数。那是错误的。我会选择Kerrek SB 的评论作为“答案”,除非有人不能这样做来发表评论。

@Howard: the code below illustrates one way to achieve what I believed the cost of a dynamically assigned deleter had to mean that unique_ptrsupported out of the box:

@Howard:下面的代码说明了一种方法来实现我认为动态分配的删除器的成本必须意味着unique_ptr支持开箱即用:

#include <iostream>
#include <memory>           // std::unique_ptr
#include <functional>       // function
#include <utility>          // move
#include <string>
using namespace std;

class Base
{
public:
    Base() { cout << "Base:<init>" << endl; }
    ~Base() { cout << "Base::<destroy>" << endl; }
    virtual string message() const { return "Message from Base!"; }
};

class Derived
    : public Base
{
public:
    Derived() { cout << "Derived::<init>" << endl; }
    ~Derived() { cout << "Derived::<destroy>" << endl; }
    virtual string message() const { return "Message from Derived!"; }
};

class BoundDeleter
{
private:
    typedef void (*DeleteFunc)( void* p );

    DeleteFunc  deleteFunc_;
    void*       pObject_;

    template< class Type >
    static void deleteFuncImpl( void* p )
    {
        delete static_cast< Type* >( p );
    }

public:
    template< class Type >
    BoundDeleter( Type* pObject )
        : deleteFunc_( &deleteFuncImpl< Type > )
        , pObject_( pObject )
    {}

    BoundDeleter( BoundDeleter&& other )
        : deleteFunc_( move( other.deleteFunc_ ) )
        , pObject_( move( other.pObject_ ) )
    {}

    void operator() (void*) const
    {
        deleteFunc_( pObject_ );
    }
};

template< class Type >
class SafeCleanupUniquePtr
    : protected unique_ptr< Type, BoundDeleter >
{
public:
    typedef unique_ptr< Type, BoundDeleter >    Base;

    using Base::operator->;
    using Base::operator*;

    template< class ActualType >
    SafeCleanupUniquePtr( ActualType* p )
        : Base( p, BoundDeleter( p ) )
    {}

    template< class Other >
    SafeCleanupUniquePtr( SafeCleanupUniquePtr< Other >&& other )
        : Base( move( other ) )
    {}
};

int main()
{
    SafeCleanupUniquePtr< Base >  p( new Derived );
    cout << p->message() << endl;
}

回答by Jagannath

This works. The destruction happens properly.

这有效。破坏正确发生。

class Base
{
    public:
     Base() { std::cout << "Base::Base\n"; }
     virtual ~Base() { std::cout << "Base::~Base\n"; }
};


class Derived : public Base
{
    public:
     Derived() { std::cout << "Derived::Derived\n"; }
     virtual ~Derived() { std::cout << "Derived::~Derived\n"; }
};

void Delete(const Base* bp)
{
    delete bp;
}

int main()
{
    std::unique_ptr<Base, void(*)(const Base*)> ptr = std::unique_ptr<Derived, void(*)(const Base*)>(new Derived(), Delete);
}