让我们说我正在编写一个包含C风格API的类。
int cstyleInit()
int cstyleCleanup()
一般来说,方法是将cstyleInit()
放在构造函数中,将cstyleCleanup()
放在析构函数中,但我的问题是,如何在构造函数中处理初始化失败?
简单的方法就是:
MyWrapper::MyWrapper() {
int ret = cstyleInit()
if (ret = FAILUREMODE1) {
cstyleCleanup();
throw exception("failure mode 1");
}
else if (ret = FAILUREMODE2) {
cstyleCleanup();
throw exception("failure mode 2");
}
...
}
据我所知,这样可行。唯一(次要)问题是你必须为每种失败模式明确地调用cstyleCleanup(),这对我来说似乎不是很好。
我知道这是一个小问题,但有更多的c ++ / RAII方式吗?
答案 0 :(得分:5)
唯一(次要)问题是你必须为每种失败模式明确地调用cstyleCleanup(),这对我来说似乎不是很好。
它与RAII没有任何关系,但可能是在包装API时要避免重复代码,以便为特定错误代码注入异常。
是否有ret != NOFAILURE
可用于此API的内容,这使您可以独立于所有这些专业调用来调用cstyleCleanup();
?
MyWrapper::MyWrapper() {
int ret = cstyleInit()
if (ret != NOFAILURE) {
cstyleCleanup();
}
// Your exception mapping code ...
}
答案 1 :(得分:2)
假设在init函数失败时也应该调用清理,你可以这样做:
namespace detail {
struct Wrapper_base
{
int ret_;
~Wrapper_base() { cstyleCleanup(); }
Wrapper_base(): ret_( cstyleInit() ) {}
};
} // namespace detail
struct Wrapper
: private detail::Wrapper_base
{
Wrapper()
{ if( ret_ != success ) { throw runtime_error( "Blah" ); } }
};
有些黑客可以优化成员变量,但最好保持这些简单。
您可以使用std::map
或您认为合适的任何内容来自定义异常消息。 C ++ 11 system_error
(如果我没记错的名字)对它有一些支持,但它具有非常特定的Unix特性。所以,如果合适,我只是使用DIY解决方案。
附录 - 删除ret_
成员变量的一种方法。
使用C ++构造函数委派:
namespace detail {
struct Wrapper_base
{
~Wrapper_base() { cstyleCleanup(); }
Wrapper_base( int& ret ) { ret = cstyleInit(); }
};
} // namespace detail
class Wrapper
: private detail::Wrapper_base
{
private:
Wrapper( int ret )
: Wrapper_base( ret )
{ if( ret != success ) { throw runtime_error( "Blah" ); } }
public:
Wrapper(): Wrapper( 0 ) {}
};
答案 2 :(得分:0)
您应该使用RAII。这意味着你没有初始化和清理功能;初始化由构造函数完成,并由析构函数清理。这可能意味着你需要一些“辅助”对象,比如Alf建议的(它应该禁用复制语义并启用移动语义;或者使用refdounting,如std :: shared_ptr)。
如果您打算退出程序,则抛出将在main()
结束时捕获的内容,并在那里输入您现有的代码。然后发生“堆栈展开”,并且首先破坏先前构造的所有对象。
如果不了解更多有关“C风格API”的内容,很难更具体。正如其他人所指出的那样,只有在init函数成功时才需要API的清理功能。您不应该混合清除此特定API以及退出程序行为。