构造函数中的异常
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++ 的样子,这就是它令人困惑的原因。
- 不要使用 "goto" 这里绝对没有理由。
- 改为使用 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_display
和 m_events
声明为引用。这些必须在构造函数的初始化列表中初始化,如下所示:
SdlManager::SdlManager(DisplayManager& display, EventManager& events)
: m_mutex(nullptr)
, m_display(display)
, m_events(events)
{
...
}
我看过一些关于这个主题的话题,但我不是很明白...
有没有人足够耐心告诉我如何使这个构造函数正确?
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++ 的样子,这就是它令人困惑的原因。
- 不要使用 "goto" 这里绝对没有理由。
- 改为使用 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_display
和 m_events
声明为引用。这些必须在构造函数的初始化列表中初始化,如下所示:
SdlManager::SdlManager(DisplayManager& display, EventManager& events)
: m_mutex(nullptr)
, m_display(display)
, m_events(events)
{
...
}