我有一个可以在其构造函数中抛出异常的类。如何在try / catch块中声明该类的实例,同时仍然可以在正确的范围内使用它?
try { MyClass lMyObject; }
catch (const std::exception& e) { /* Handle constructor exception */ }
lMyObject.DoSomething(); // lMyObject not in scope!
是否有另一种方法可以实现这一点,同时尊重RAII成语?
我不想使用init()
方法进行两阶段构建。我能想到的另一件事是:
MyClass* lMyObject;
try { lMyObject = new MyClass(); }
catch (const std::exception& e) { /* Handle constructor exception */ }
std::shared_ptr<MyClass> lMyObjectPtr(lMyObject);
lMyObjectPtr->DoSomething();
正常工作,但我对范围和指针间接的原始指针不满意。这只是另一个C ++疣吗?
答案 0 :(得分:5)
如果构造函数抛出,则意味着对象初始化失败,因此无法启动它。
MyClass* lMyObject;
try { lMyObject = new MyClass(); }
catch (std::exception e) { /* Handle constructor exception */ }
在上面,如果构造函数抛出异常,lMyObject
保持未初始化,换句话说,指针包含一个不确定的值。
有关详细说明,请参阅经典Constructor Failures :
我们可以按如下方式总结C ++构造函数模型:
或者:
(a)构造函数通常通过到达它的结尾或return语句返回,并且该对象存在。
或者:
(b)构造函数通过发出异常退出,该对象现在不仅不存在,而且从不存在。
没有其他可能性。
答案 1 :(得分:2)
编写代码的最佳方法是: -
MyClass lMyObject;
lMyObject.DoSomething();
没有特洛伊,捕获或指针。
如果构造函数抛出,则无法调用DoSomething。哪个是对的:如果构造函数抛出,则对象从未构造过。
而且,重要的是,不要捕捉(甚至捕获/重新抛出)异常,除非你有一些建设性的东西。让异常完成他们的工作并起作用,直到知道如何处理它们的东西能够完成它的工作。
答案 2 :(得分:1)
构造函数用于将对象置于一致状态并建立类不变量。允许异常转义构造函数意味着该构造函数中的某些内容未能完成,因此现在该对象处于某种未知的奇怪状态(现在也可能包括资源泄漏)。由于对象的构造函数尚未完成,编译器也不会导致它的析构函数被调用。也许您正在寻找的是捕获构造函数中的异常。假设它没有被重新抛出,这将导致构造函数完成执行,并且该对象现在已完全形成。
答案 3 :(得分:0)
您无需使用shared_ptr
,请使用unique_ptr
:
std::unique_ptr<MyClass> pMyObject;
try { pMyObject.reset(new MyClass()); }
catch (std::exception &e) { /* Handle constructor exception */ throw; }
MyClass &lMyObject = *pMyObject;
lMyObject.DoSomething();
显然,您有责任确保程序不会落入catch
块,而无需初始化pMyObject
或退出该功能(例如通过return
或{{1} })。
如果可用,您可以使用Boost.Optional来避免使用堆内存:
throw
答案 4 :(得分:-1)
您可以设置MyClass的复制构造函数来接受垃圾输入,从而有效地声明一个带有对象声明的指针。然后你可以manually call try块中的默认构造函数:
MyClass lMyObject(null); // calls copy constructor
try {
new (lMyObject) MyClass(); // calls normal constructor
}
catch (const std::exception& e) { /* Handle constructor exception */ }
lMyObject.DoSomething();