【问题标题】:Is it possible to use SDL2 with smart pointers?是否可以将 SDL2 与智能指针一起使用?
【发布时间】:2018-07-18 06:00:43
【问题描述】:

我有这行代码

//std::unique_ptr<SDL_Window> _window_; // this is somewhere else...
_window_ = std::make_unique<SDL_Window>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _WIDTH_, _HEIGHT_, SDL_WINDOW_SHOWN));

它会产生以下编译器错误

In file included from /usr/include/c++/6/memory:81:0,
                 from /home/user/prj/src/main.cpp:4:
/usr/include/c++/6/bits/unique_ptr.h: In instantiation of ‘typename 
std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = SDL_Window; _Args = {SDL_Window*}; typename 
std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<SDL_Window>]’:
/home/user/prj/src/main.cpp:36:170:   required from here
/usr/include/c++/6/bits/unique_ptr.h:791:30: error: invalid use of incomplete type ‘struct SDL_Window’
     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }

为什么? (没有智能指针它也能正常工作,所以我猜我不理解语法,这很容易解决。将在下面添加源代码和CMakeLists.txt。)

CMakeLists.txt

cmake_minimum_required(VERSION 3.7)
project(prj)

find_package(SDL2 REQUIRED)
include_directories(prj ${SDL2_INCLUDE_DIRS})

add_executable(prj main.cpp)
target_link_libraries(prj ${SDL2_LIBRARIES})

ma​​in.cpp

#include "SDL.h"
#include <memory>
#include <iostream>
#include <fstream>
#include <cstdint>

class Window
{

    public:

    Window()
        : _window_{nullptr}
        , _surface_{nullptr}
    {
        if(SDL_Init(SDL_INIT_VIDEO) < 0)
        {
            std::cerr << SDL_GetError() << std::endl;
        }
        else
        {
            _window_ = std::make_unique<SDL_Window>(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _WIDTH_, _HEIGHT_, SDL_WINDOW_SHOWN));

            if(_window_ == nullptr)
            {
                std::cerr << SDL_GetError() << std::endl;
            }
            else
            {
                _surface_ = std::make_unique<SDL_Surface>(SDL_GetWindowSurface(_window_.get()));
                SDL_FillRect(_surface_.get(), nullptr, SDL_MapRGB(_surface_->format, 0xFF, 0xFF, 0xFF));
                SDL_UpdateWindowSurface(_window_.get());
                SDL_Delay(1000);
            }
        }
    }

    ~Window()
    {
        SDL_DestroyWindow(_window_.get());
        SDL_Quit();
    }

    private:

    const int32_t _WIDTH_{600};
    const int32_t _HEIGHT_{400};

    std::unique_ptr<SDL_Window> _window_;
    std::unique_ptr<SDL_Surface> _surface_;

};


int main(int argc, char* argv[])
{
    Window window;
    return 0;
}

【问题讨论】:

  • @lcs 已编辑问题
  • struct SDLWindowDestroyer { void operator()(SDL_Window* w) { SDL_DestroyWindow(w); } }; using SDLWindowPtr = std::unique_ptr&lt;SDLWindow, SDLWindowDestroyer&gt;;
  • @user3728501 该问题的答案包含语法。
  • @lcs 他们使用不同的语法来使用结构。在这种情况下,我的问题变成“我的尝试有什么问题”。

标签: c++ sdl smart-pointers sdl-2 unique-ptr


【解决方案1】:

解决方案

经过反复试验,终于找到了答案,所以在这里解释一下解决方案。

这是正确的语法:

// first define the unique_ptr as member of class
std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> _window_;

// second, initialize in the member initialization list of class constructor
// probably don't need to do this if not embedding as member of class
class_name()
    : _window_(nullptr, SDL_DestroyWindow)
{
    // blaa blaa SDL code etc
    _window_.reset(SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN));
}

// finally we need to be able to delete
// but this is handled automatically

说明

当我们将 unique_ptr 添加为数据成员时,我们需要同时给出类型 SDL_Window 和“删除函数格式/语法”,因为普通的 delete 调用是不正确的。我们使用decltype从deleter函数中自动构造正确的deleter格式。 (也许不是最准确的解释。)在某种程度上,decltype 有点像汽车......

std::unique_ptr<SDL_Window, decltype(&SDL_DestroyWindow)> _window_;

这个对象必须被初始化。我们在构造函数中这样做。我们将指针设置为nullptr(因为我们不想在初始化SDL2之前对其进行初始化)并且我们还设置了deleter函数。

: _window_(nullptr, SDL_DestroyWindow)

初始化 SDL 后,我们要创建一个窗口。这通过调用智能指针reset() 函数最容易完成。我们将创建窗口的函数返回的新指针传递给它。

_window_.reset(SDL_CreateWindow(...));

完成。花了很长时间才弄清楚,但现在有道理了。参考文献

http://en.cppreference.com/w/cpp/memory/unique_ptr

Why does my unique_ptr think is has a null function pointer deleter?

What's wrong with this initialization of unique_ptr?

【讨论】:

  • 自定义删除器是您想要的,但请注意您的代码可以进一步改进:coliru.stacked-crooked.com/a/adc9e7f6bf45e0ca
  • @milleniumbug 怎么样?对我来说,这看起来不像是一种改进
  • 参考链接。使用无状态的函数对象减少了智能指针的大小,并且不需要将函数传入std::unique_ptr的构造函数@
  • 请注意,您不需要构造函数初始化程序。你可以直接内联初始化_window_:std::unique_ptr&lt;SDL_Window, decltype(&amp;SDL_DestroyWindow)&gt; _window_{nullptr, SDL_DestroyWindow};
  • @milleniumbug 道歉。我错过了这里的重点(缩小尺寸。)
【解决方案2】:

使用 SDL_DestroyWindow 销毁 SDL 窗口,而不是像 unique_ptr 的默认删除器那样直接删除。您需要为 unique_ptr 提供一个自定义删除器,它将调用 SDL_DestroyWindow。

【讨论】:

    【解决方案3】:

    这些结构是opaque data structures,您没有它们的完整定义。这意味着默认的std::unique_ptr 删除器不知道如何“删除”这些结构。

    您需要提供自己的自定义删除器。对于SDL_Window,它是SDL_DestroyWindow 函数。

    【讨论】:

    • 这也意味着在这种情况下你不使用make_unique。
    猜你喜欢
    • 2011-03-29
    • 2021-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    • 2019-01-27
    • 2014-07-31
    相关资源
    最近更新 更多