如何清理失败的构造函数

时间:2014-06-05 00:37:00

标签: c++ raii

让我们说我正在编写一个包含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方式吗?

3 个答案:

答案 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以及退出程序行为。