std::shared_ptr 删除器类型

std::shared_ptr Deletor Type

我在尝试将自定义删除器传递给 std::shared_ptr 时遇到奇怪的错误:

std::shared_ptr<unsigned char*> SDLWindow::drawGrid(const Grid* grid) { 
    SDL_Surface* rgbSurface = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGB888, 0);
    //...error checking and locking the SDL_Surface, omitted for brevity
    unsigned char* pixelsPtr = (unsigned char*)(rgbSurface->pixels);

    //need a custom deleter because we created a copy of the SDL_Surface
    //we cant directly delete the pixel data but need to delete the underlying SDL_Surface instead
    auto surfaceDeleter = [rgbSurface](decltype(pixelsPtr)* ignored) 
            //don't directly delete the pixel buffer, delete the underlying SDL_Surface instead
            {
                //unlock the surface if necessary
                if(SDL_MUSTLOCK(rgbSurface))
                {
                    SDL_UnlockSurface(rgbSurface);
                }
                SDL_FreeSurface(rgbSurface);
            };
    return std::shared_ptr<unsigned char*>(pixelsPtr, surfaceDeleter);
}

(我更改了 const unsigned char* -> unsigned char* 以防与它有任何关系,但如果可能的话我更愿意使用 const)

Clang 说了一些关于无法转换为 nullptr_t 的事情,我认为这与 shared_ptrs 如何使用类型擦除有关(但为什么他们不能处理指针?)

    unsigned char* pixelsPtr = (unsigned char*)(rgbSurface->pixels);
[..]/src/SDLWindow.cpp:132:12: error: no matching constructor for initialization of 'std::shared_ptr<unsigned char *>'
    return std::shared_ptr<unsigned char*>(pixelsPtr, surfaceDeleter);
           ^                               ~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:3809:26: note: candidate constructor [with _Dp = (lambda at
      /[..]/src/SDLWindow.cpp:122:27)] not viable: no known conversion from 'unsigned char *' to 'nullptr_t' for 1st argument
    template <class _Dp> shared_ptr(nullptr_t __p, _Dp __d);

奇怪的是 g++ 似乎给出了一个完全不同的错误:

                 from /[..]/src/SDLWindow.hpp:4,
                 from /[..]/src/SDLWindow.cpp:1:
/opt/local/include/gcc49/c++/bits/shared_ptr_base.h: In instantiation of 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Tp1*, _Deleter) [with _Tp1 = unsigned char; _Deleter = jakway_antf::SDLWindow::drawGrid(const jakway_antf::Grid*)::<lambda(unsigned char**)>; _Tp = unsigned char*; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]':
/opt/local/include/gcc49/c++/bits/shared_ptr.h:130:37:   required from 'std::shared_ptr<_Tp>::shared_ptr(_Tp1*, _Deleter) [with _Tp1 = unsigned char; _Deleter = jakway_antf::SDLWindow::drawGrid(const jakway_antf::Grid*)::<lambda(unsigned char**)>; _Tp = unsigned char*]'
/[..]/src/SDLWindow.cpp:132:69:   required from here
/opt/local/include/gcc49/c++/bits/shared_ptr_base.h:881:37: error: cannot convert 'unsigned char*' to 'unsigned char**' in initialization
  : _M_ptr(__p), _M_refcount(__p, ntf::SDLWindow::drawGrid(const jakway_antf::))

关于 g++ 我不知道发生了什么,因为我弄乱了 lambda 的参数并声明它 unsigned char** 没有做任何事情。

我试过:

std::shared_ptr<unsigned char*> 应该是 std::shared_ptr<unsigned char>。使用 std::shared_ptr<unsigned char*> 意味着底层指针是 unsigned char**,因为 shared_ptr<T> 存储 T*。由于您有一个 unsigned char*,我们希望 T 成为 unsigned char

有一种更简洁的方法来做你想做的事,使用共享指针的一个非常棒且文档不足的特性:

// 1
std::shared_ptr<SDL_Surface> surface_ptr {
    SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGB888, 0),
    [](SDL_Surface* p) {
        if(SDL_MUSTLOCK(p))
            SDL_UnlockSurface(p);
        SDL_FreeSurface(p);
    }
};

// 2
std::shared_ptr<unsigned char> pixels = { surface_ptr, surface->pixels };

第 1 行分配表面并将其置于具有自定义删除器的共享指针的控制之下。

第 2 行是很棒的部分。它初始化一个共享指针指向像素数据,同时使用与表面相同的控制块

这意味着只有当两个共享指针都被销毁或重置时,表面才会被删除。它是保存引用计数和删除器的控制块,因此您不必担心两个共享指针指向不同的类型——第二个只是第一个的方便外观。

请注意,您不必在 lambda 中捕获任何内容 - shared_ptr 会为您完成此操作并将表面地址作为参数传递。