构造函数中的异常

Exception in constructor

我看过一些关于这个主题的话题,但我不是很明白...

有没有人足够耐心告诉我如何使这个构造函数正确?

SdlManager::SdlManager() {

    //SDL init
    if(SDL_Init(SDL_INIT_VIDEO) < 0) {
      std::cerr << "Could not initialize SDL: " << SDL_GetError() << std::endl;
      goto error1;
    }

    //Init mutex for SDL access
    m_mutex = SDL_CreateMutex();
    if (m_mutex == nullptr)
      goto error2;

    try {
      m_display = DisplayManager(m_mutex, false);
      m_events = EventManager(m_mutex);
    }
    catch (void* _) {
      goto error3;
    }
    return;

    //Error Handling
  error3:
    SDL_DestroyMutex(m_mutex);
  error2:
    SDL_Quit();
  error1:
    throw ;
  }

欢迎任何建议。

谢谢 祝你有个愉快的一天。

编辑:

关于错误:

src/sdl_manager.cpp:6:1: error: uninitialized reference member in ‘class sdl::DisplayManager&’ [-fpermissive]
     SdlManager::SdlManager() {
     ^
    In file included from src/sdl_manager.cpp:2:0:
    ./src/include/sdl_manager.hpp:13:20: note: ‘sdl::DisplayManager& sdl::SdlManager::m_display’ should be initialized
        DisplayManager& m_display;
                        ^
    src/sdl_manager.cpp:6:1: error: uninitialized reference member in ‘class sdl::EventManager&’ [-fpermissive]
     SdlManager::SdlManager() {
     ^
    In file included from src/sdl_manager.cpp:2:0:
    ./src/include/sdl_manager.hpp:14:19: note: ‘sdl::EventManager& sdl::SdlManager::m_events’ should be initialized
        EventManager&  m_events;
                       ^

看来我的try-catch块不好

编辑 2:

我只是把sdl_manager.hpp确定一下。

#ifndef  SDL_MANAGER_HPP
#define  SDL_MANAGER_HPP

#include  <SDL2/SDL_mutex.h>
#include  "display_manager.hpp"
#include  "event_manager.hpp"

namespace  sdl
{
  class  SdlManager {
    private:
      SDL_mutex*      m_mutex;
      DisplayManager& m_display;
      EventManager&   m_events;

    public:
      SdlManager();
      ~SdlManager();
  };
}
#endif

所以,这不是现代惯用的 C++ 的样子,这就是它令人困惑的原因。

  1. 不要使用 "goto" 这里绝对没有理由。
  2. 改为使用 RAII 习语来简化这些东西。

基本上在上面的代码中,您使用的是 "goto" 而不是析构函数。这就是基于 RAII 习语的代码的样子。

struct Sdl_error : public std::exception {
  ...
};

struct Sdl_Init_RAII {
  Sdl_Init_RAII()
  {
    if(SDL_Init(SDL_INIT_VIDEO) < 0) {
      std::cout << "Could not initialize SDL: " << SDL_GetError() << std::endl;
      throw Sdl_error();
    }
  }

  ~Sdl_Init_RAII()
  {
    SDL_Quit();
  }
};

struct Sdl_Mutex {
  SDL_Mutex * m_ptr;

  Sdl_Mutex()
    : m_ptr(SDL_CreateMutex())
  {
    if (m_ptr == nullptr) throw Sdl_error();
  }
  ~Sdl_Mutex()
  {
    if (m_ptr) { SDL_DestroyMutex(m_ptr); }
  }
};

SdlManager::SdlManager
  : m_init()
  , m_mutex()
  , m_displayer(m_mutex.m_ptr, false)
  , m_events(m_mutex.m_ptr)
{}

请注意,您稍微更改了发布的代码示例,但您应该将名为 m_init 的类型 Sdl_Init_RAII 的成员添加到 SdlManager 以使上面的代码片段有意义.

注意SdlManager在这里不需要有析构函数,因为它本身并不直接管理任何C资源。但是,display、events、mutex 都可以。 Sdl_Init_RAII 也存在,即使它没有成员变量——它的目的是强制执行合同,即每当我们调用 C 函数 SDL_Init 时,我们稍后也会调用 SDL_Quit。每当你有一个 C 库强加了一些这样的要求时,使用 RAII 对象来确保你的 C++ 程序符合要求通常是个好主意。

编辑:现在您明确发布了错误消息,我可以看到我的重构也会修复它。问题是,当你有一个带有引用成员变量的构造函数时,你必须在初始化列表中初始化它,否则它不能在函数开始时绑定到任何东西(!),而语言只是不允许,禁止是整个参考点。

错误 uninitialized reference member 表示您将 m_displaym_events 声明为引用。这些必须在构造函数的初始化列表中初始化,如下所示:

SdlManager::SdlManager(DisplayManager& display, EventManager& events)
: m_mutex(nullptr)
, m_display(display)
, m_events(events)
{
    ...
}