2014-09-25 51 views
0

你好民间的StackOverflow。有条件地处理成员变量初始化期间的异常

有没有更好的方法处理成员变量的构造函数中的异常?我不得不与一个库类进行交互,它可能会或可能不会在它的构造函数中抛出异常(不能提前检查),我想避免在我的类中使用指针(如果发生崩溃,我希望全部即使我搞砸了,也可以正确调用析构函数)。我已经目前此实现结算(包括了一个实例的虚拟存根):

class class_I_have_no_control_over{ 
    public: 
    class_I_have_no_control_over(int someArgument) 
    { 
     if(someInternalConditionThatCantBeTestedExternally) 
      throw anException; 
    } 

    class_I_have_no_control_over() 
    { //Does not throw 
    } 
} 

class MyClass{ 
    private: 
    class_I_have_no_control_over memberVariable; 

    public: 
    MyClass() 
    { 
     try{ 
      class_I_have_no_control_over tempVariable(24); 
      memberVariable = std::move(tempVariable); 
     }catch(...) 
     { 
      class_I_have_no_control_over tempVariable(); 
      memberVariable = std::move(tempVariable); 
     } 
    } 
} 

我认为第一种方法是尝试捕获的初始化列表:即

class MyClass{ 
    private: 
    OtherClassThatTrowsOnConstruct member; 

    MyClass() 
     try: 
     member() 
     {//Normal constructor 
     } 
     catch(...) 
     {//Can translate an exception but cant stop it. 
     } 

但只能用这种方法翻译异常,而不是阻止它们(如果你不抛出异常,运行时将重新抛出原始异常)。

有人会说使用动态分配(即与新的和删除关键字的指针),但由于这个库处理进程之间的共享内存,我有点厌倦了在发生崩溃时动态内存内容会发生什么在其中一个应用程序中(例如析构函数从不调用,另一个应用程序正在等待不再运行的应用程序永远不会意识到它不再监听)。

+0

移动操作表明'class_I_have_no_control_over'使用动态内存分配。然后什么阻止你做同样的事情。无论如何,你总是可以使用就地建造(放置新的),并使用合适的缓冲区。 – 2014-09-25 15:37:22

+0

实际上,你确定'catch'里面的'class_I_have_no_control_over tempVariable();'总是会成功吗? – 2014-09-25 15:39:23

+0

@πάνταῥεῖ:该构造函数中的“不抛出”的注释。 – 2014-09-25 15:39:57

回答

0

第一个版本会有所简化,不改变其行为:

MyClass() try { 
    memberVariable = class_I_have_no_control_over(24); // move from temporary 
} catch (...) { 
    // no need to do anything; memberVariable is already default-constructed 

    // Unless the class is so evil that move-assignment might fail and leave 
    // the target in a messed-up state. In which case you probably want 
    memberVariable = class_I_have_no_control_over(); 
    // If that fails, you should probably give up and let the exception go. 
} 

除非你有进一步的限制(例如无法移动类),这是对付你的情况最好的方式。如果它不可移动,那么动态分配可能是一个合理的选择;使用智能指针,可能是std::unique_ptr,以确保它与MyClass对象一起销毁。

#include <memory> 

class MyClass{ 
    private: 
    std::unique_ptr<unmovable_evil> memberVariable; 

    public: 
    MyClass() try { 
     memberVariable.reset(new unmovable_evil(24)); 
    } catch(...) { 
     memberVariable.reset(new unmovable_evil); 
    } 
}; 

您也可以考虑将boost::optional作为动态分配的非标准替代方案。

+0

重申“不需要做任何事”评论,不,成员变量可以彻底搞砸,如果24建设成功但分配失败。 OP的代码也没有考虑到混乱的可能性,而是以更直接/更明显的方式。智能指针解决方案似乎没问题。 re boost :: optional,一个简单的方法可以在没有boost依赖的情况下做同样的事情,就是使用std :: vector和emplace_back来进行构造尝试。 – 2014-09-25 16:51:01

+0

@ Cheersandhth.-Alf:是的,我确实忘记了没有提供强有力的例外保证的移动分配的可能性;但这不会是一个问题,除非它做的事情很奇怪。 – 2014-09-25 16:54:08

+0

@ Cheersandhth.-Alf:一个vector不会比'unique_ptr'提供任何优势;该对象仍然是动态分配的。 – 2014-09-25 16:58:03