使用unique_ptr和自定义deleter时感到困惑

Confused using unique_ptr and a custom deleter

本文关键字:deleter 自定义 unique ptr 使用      更新时间:2023-10-16

我正在尝试将unique_ptrSDL_Surface类型的自定义deleter一起使用。这只是一个使用int类型的示例,但我希望您能理解。

#include <iostream>
#include <functional>
#include <memory>
typedef int SDL_Surface;

SDL_Surface * CreateSurface()
{
    SDL_Surface * p = new SDL_Surface;
    return p;
}
void FreeSurface(SDL_Surface *p)
{
    delete p;
}
int main() {
    std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *) > > uptr_1; 
    //how to assign a value to uptr_1 and the deleter? 
    return 0;
}

uptr_1是否正确声明并初始化为nullptr?如果是,如何分配指针和deleter函数?

我如何概括这一点:std::unique_ptr< SDL_Surface, std::function< void (SDL_Surface *) > >和deleter不总是在我想要的每个SDL_Surface上写那一行,另一个typedef?

我刚开始学习C++11的功能,这对我来说很难。

您可以用指针和deleter初始化unique_ptr,或者如果稍后重新分配,则正常使用=

std::unique_ptr<SDL_Surface, std::function<void (SDL_Surface *)>> uptr_1(CreateSurface(), &FreeSurface);
uptr_1 = std::unique_ptr<SDL_Surface, std::function<void (SDL_Surface *)>>(CreateSurface(), &FreeSurface);

有关详细信息,请参阅合适的文档。

为了缩短长类型,您确实可以使用类型别名(typedefusing):

typedef std::unique_ptr<SDL_Surface, void (*)(SDL_Surface*)> Surface_ptr;
//or
using Surface_ptr = std::unique_ptr<SDL_Surface, void (*)(SDL_Surface*)>;

请注意,我实际上已经将void (*)(SDL_Surface*)用于deleter类型。如果您知道您总是会传入一个实际的函数(或无状态lambda),那么就没有理由在std::function中拖动,因为类型擦除会带来一些开销。

此外,您还可以通过为deleter创建一个默认的可构造函子来进一步缩短它:

struct FreeSurface_Functor
{
  void operator() (SDL_Surface *s) const
  {
    FreeSurface(s);
  }
};

这样,您就可以使指针的类型为std::unique_ptr<SDL_Surface, FreeSurface_Functor>(可能是别名),而不必提供deleter;它将默认构造:

std::unique_ptr<SDL_Surface, FreeSurface_Functor> uptr_1(CreateSurface());

我会选择decltype:

std::unique_ptr<SDL_Surface, decltype(&FreeSurface)> uptr_1(
          CreateSurface(),
          FreeSurface
);

uptr_1是否正确声明并初始化为nullptr

是的,默认构造的unique_ptr将引用null。

如果是,我如何分配指针和deleter函数?

您应该使用参数构建unique_ptr

 std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *) > > uptr_1{CreateSurface(), FreeSurface};

或者,在默认构造之后,您可以使用带有临时的移动分配

uptr_1 = std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *) > >{CreateSurface(), FreeSurface};

正如您自己所建议的,类型别名可以帮助

using SDL_Uptr = std::unique_ptr<SDL_Surface, std::function< void (SDL_Surface *)>>;
SDL_Uptr  uptr_1;
uptr_1 = SDL_Uptr{CreateSurface(), FreeSurface};

如果中间函数变得重复(如果你制作了很多中间函数,它可能会重复),那么它可以帮助简化这个过程。

std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)>
make_sdl_ptr() {
    return std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)>{CreateSurface(), FreeSurface};
}

然后你可以用auto uptr = make_sdl_ptr(); 来调用它

Angew的答案是用DefaultConstructable deleter调用函数,这也是一个非常好的解决方案。