那么,std :: unique_ptr的自定义删除工作如何工作?

Che*_*Alf 55 c++ unique-ptr delete-operator c++11

根据N3290 std::unique_ptr在其构造函数中接受一个删除参数.

但是,我不能在Windows中使用Visual C++ 10.0或MinGW g ++ 4.4.1,也不能在Ubuntu中使用g ++ 4.6.1.

因此,我担心我对它的理解是不完整或错误的,我不能看到显然被忽略的删除论证的观点,那么任何人都可以提供一个有效的例子吗?

我最好还想看看它是如何工作的unique_ptr<Base> p = unique_ptr<Derived>( new Derived ).

可能会使用标准中的一些措辞来备份示例,即使用您正在使用的任何编译器,它实际上做了它应该做的事情?

Ben*_*ley 45

这适用于MSVC10

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

在gcc 4.5上,这里

我会跳过标准,除非你不认为这个例子完全符合你的期望.

  • 为什么不只是"std :: unique_ptr <int,decltype(del)> px(&x);" ? (2认同)
  • @Jon:因为lambda类型没有默认构造函数. (2认同)
  • 对于试图学习如何使用自定义删除器的人,此答案可能会造成混淆,因为只有当由unique_ptr管理的对象在创建范围的末尾被销毁时,decltype技巧才起作用。如果您的unique_ptr存储为成员变量或从函数返回,则无法表达该decltype,而您需要在其中使用std :: function。 (2认同)

Phi*_*tte 25

为了补充所有以前的答案,有一种方法可以有一个自定义删除器,而不必通过在其中使用函数指针或类似的东西来"污染"unique_ptr签名,如下所示:

std::unique_ptr< MyType, myTypeDeleter > // not pretty
Run Code Online (Sandbox Code Playgroud)

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

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

现在所有std::unique_ptr< MyType >"看到"这个专业化的东西都将被删除.请注意,它可能不是您想要的所有内容std::unique_ptr< MyType >,因此请仔细选择您的解决方案.

  • 专门化std模板是合法的,而且不是一个坏习惯,但是您需要遵循“规则”,请参阅[this post](http://stackoverflow.com/questions/8513417/what-c​​an-and-cant-i-专用于标准命名空间)。std :: default_delete是模板专业化的理想选择。 (3认同)

Che*_*Alf 11

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

但是,万一人们想知道,我错误地认为a unique_ptr<Derived>可以移动到a unique_ptr<Base>然后会记住Derived对象的删除,即Base不需要虚拟析构函数.那是错的.我选择Kerrek SB的评论作为"答案",除了评论不能做到这一点.

@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;
}
Run Code Online (Sandbox Code Playgroud)

干杯,

  • @stijn:只要一些其他机制(例如自定义删除器)完成识别派生类最多的工作,析构函数在技术上就不需要是虚拟的.然后使其成为非虚拟的一个正当理由是保持与外部强加的内存布局的兼容性,即您不希望在内存区域的前面有vtable ptr.另一个有效的原因是,如果破坏总是应该通过删除器,那么使析构函数虚拟将错误地指示可以使用C++`delete`,或者至少有一些原因. (3认同)

Jag*_*ath 6

这有效.破坏恰当.

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);
}
Run Code Online (Sandbox Code Playgroud)