C++ C++;堆栈对象的构造函数异常处理
如果我希望有一个在构造期间可能抛出的堆栈分配对象,但希望在调用站点处理异常,那么如何使该对象可以从构造它的try块外部访问 乙二醇 如果我将myObj封装在一个try中,那么try范围之外的任何东西都看不到它,但我不想让其他所有东西都在其中,因为这样代码就变成了30层嵌套的try块,这就是异常处理应该使用init函数错误代码的替代方法来移除的 我无法在构造函数内处理异常,因为对异常的反应取决于MyThrowingClass使用的上下文 显然,这个问题可以通过有一个C++ C++;堆栈对象的构造函数异常处理,c++,exception,scope,stack,allocation,C++,Exception,Scope,Stack,Allocation,如果我希望有一个在构造期间可能抛出的堆栈分配对象,但希望在调用站点处理异常,那么如何使该对象可以从构造它的try块外部访问 乙二醇 如果我将myObj封装在一个try中,那么try范围之外的任何东西都看不到它,但我不想让其他所有东西都在其中,因为这样代码就变成了30层嵌套的try块,这就是异常处理应该使用init函数错误代码的替代方法来移除的 我无法在构造函数内处理异常,因为对异常的反应取决于MyThrowingClass使用的上下文 显然,这个问题可以通过有一个 MyThrowingClass
MyThrowingClass* pMyObj;
然后能够包装
pMyObj = new MyThrowingClass();
但这当然也可以通过堆栈分配的对象来实现吗
这是唯一的解决办法
MyThrowingClass myObj;
try {
myObj.init();
} catch(...) {
//...
}
在这一点上,我们回到了基本上与错误代码一样糟糕的状态,有一个未初始化或部分初始化的对象
请注意,这并不是一个全局对象,我希望有一些东西可以在很多地方实例化
有一个try块包装整个范围(这里是main中的所有内容),并捕获在一个try块的末尾处理每个可能的异常,而不是在其站点附近模糊地处理异常,这真的是理想的解决方案吗
int main() {
try {
//absoultely everything
}
catch (exceptionTypeA &a) {
//...
}
catch exceptionTypeB &b) {
}
}
如何使对象可以从构建它的try块外部访问
如果构造失败,则对象不存在;所以没有什么可访问的
当然,使用堆栈分配的对象也可以实现这一点
自动(即堆栈分配)对象只初始化一次,因此即使处理异常,也无法返回并尝试重新初始化它。如果您确实希望能够重试,那么您必须使用更复杂的方法,比如您建议的动态分配或两阶段初始化。另一种选择是某种boost::optional
(或者,从明年开始,std::optional
),它允许您在一个自动存储区内随意创建和销毁对象
拥有一个覆盖整个范围的try块真的是理想的解决方案吗
在典型情况下,如果本地未处理异常,并且初始化失败表示出现不可恢复的错误,则为“是”。在您的特殊情况下,您可以在本地处理它并进行恢复。否。
try
旨在确定可能因某种原因引发的对象的范围。通过绕过它,你绕过了它试图保护你的东西(使用一个定义不好的对象)。考虑使用函数来生成对象。通过使用noexcept
move构造函数,您可以保证移出对象是安全的:
class MyThrowingClass {
public:
MyThrowingClass() {
throw exception();
}
// throw() is *okay* if you don't have noexcept
MyThrowingClass(const MyThrowingClass && other) noexcept {
}
};
MyThrowingClass GetObj() {
try {
return std::move(MyThrowingClass());
} catch(...) {
// return some well defined default or terminate program
}
}
int main() {
MyThrowingClass myObj(std::move(GetObj()));
}
如果我希望有一个在构造期间可能抛出的堆栈分配对象,但希望在调用站点处理异常,那么如何使该对象可以从构造它的try块外部访问
基本上,你不能。至于将所有代码包装在一个try块中是一个好主意还是坏主意,这取决于“所有代码”的大小——十几行左右的代码没什么大不了的
如果初始化器抛出,是否确实要调用MyThrowingClass::doSomethingImportant()
?除非您以某种方式保证修复catch
中损坏的初始化,否则您将对部分初始化的对象调用方法
将对
doSomethingImportant()
的调用与对象的构造包含在同一个try块中,将为您提供设计异常的确切功能:在出现问题时,跳过以下代码(依赖于前面的代码)到错误处理程序。理想情况下,您应该将对myObj
的所有引用放在try
块中。否则,您的指针解决方案是唯一的选项。好的,这说明了一个极好的观点,即如果初始化失败,堆栈分配的对象不可能再次构造,因此即使有对象失败的信息,您也无法修复它。因此,从对象派生的所有使用都应该在try块中,因为这是确保在您尝试使用该对象时该对象存在的唯一方法。指针初始化的方式可以重试,因此可以在try{}的分数之外使用它。谢谢。我认为这里的关键是@mike seymour指出的:如果构造函数失败,就没有办法修复对象,因此能够从try{}范围之外访问对象是毫无意义/危险的。如果您可以修复它,那么这将使它成为一个理想的特性,就像在指针情况下修复对象是可行的一样。
class MyThrowingClass {
public:
MyThrowingClass() {
throw exception();
}
// throw() is *okay* if you don't have noexcept
MyThrowingClass(const MyThrowingClass && other) noexcept {
}
};
MyThrowingClass GetObj() {
try {
return std::move(MyThrowingClass());
} catch(...) {
// return some well defined default or terminate program
}
}
int main() {
MyThrowingClass myObj(std::move(GetObj()));
}