如果您想朝着“看起来和感觉像现代C ++”的方向前进,可以结合以下两种技术:
- 作用域守卫(用于
SDL_Quit
),
- 专门的默认删除器(用于
SDL_Window
)。
作用域守卫
作用域守卫是一个虚拟对象,其从其析构函数中调用提供的函数对象。在堆栈上分配它将使它在离开定义范围时调用析构函数;在
main()
函数中执行此操作意味着它将在程序退出时运行。有关更多详细信息,请参见Andrei Alexandrescu的
演讲(有关作用域守卫的部分从1:05:14开始)。
实现(主要来自演示):
template<class Fn>
class ScopeGuard {
public:
explicit ScopeGuard(Fn fn) noexcept
: fn_(std::move(fn)),
active_(true) {}
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard &) = delete;
ScopeGuard(ScopeGuard &&that) noexcept
: fn_(std::move(that.fn_)),
active_(that.active_) {
that.dismiss();
}
ScopeGuard &operator=(const ScopeGuard &) = delete;
~ScopeGuard() {
if (active_) {
try {
fn_();
} catch (...) {
}
}
}
void dismiss() noexcept {
active_ = false;
}
private:
Fn fn_;
bool active_;
};
直接实例化类可能不太方便,但在函数中我们可以获得类型推断:
template<class Fn>
ScopeGuard<Fn> scopeGuard(Fn fn) {
return ScopeGuard<Fn>(std::move(fn));
}
要创建一个作用域保护,你只需要调用 scopeGuard(lambda)
,其中 lambda
是你想在离开作用域时运行的函数。实际类型将被推断;我们不关心它。
// Will call SDL_Quit() once 'guard' goes out of scope.
auto guard = scopeGuard([] { SDL_Quit(); });
专用默认删除器
您可以通过定义以下函数对象(在本例中为具有operator()
的结构体)来专门化std::default_deleter
:
template<>
struct std::default_delete<SDL_Window> {
void operator()(SDL_Window *p) { SDL_DestroyWindow(p); }
};
对于大多数SDL类型,您可以这样做,因为它们都有一个“destroy”函数。(您可能希望将其包装在宏中。)
好处在于,从SDL 2.0.x开始,我们从SDL2 / SDL.h
获取的SDL_Window
和其他类型都是不完整的类型,即您无法在它们上调用sizeof(SDL_Window)
。这意味着编译器将无法直接delete
它们(需要sizeof
),因此您将无法实例化(普通的)std :: unique_ptr<SDL_Window>
。
但是,使用专门的删除器,std :: unique_ptr<SDL_Window>
将起作用,并且将在析构函数中调用SDL_DestroyWindow
。
结果
使用上面的定义,结果是一个非常简洁的RAII示例:
int main()
{
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
return 1;
}
auto quit_scope_guard = scopeGuard([] { SDL_Quit(); });
std::unique_ptr<SDL_Window> win(SDL_CreateWindow("asdf", 100, 100,
640, 480, SDL_WINDOW_SHOWN));
if (!win) {
return 1;
}
return 0;
}
win
超出作用域时,即在SDL_Quit
之后,此代码将调用SDL_DestroyWindow
,这可能不是您想要的。 - HoltSDL_CreateWindow
不是子系统,所以应该没问题。此外还有一些解决方法,比如使用atexit()
。 - user6326610