C++异常处理混乱
C++ exception handling confusion
所以,基本上我有一个外部 C 库的简单包装代码,我是正确异常处理方面的新手。
提前说明:代码两次显示相同的问题,但 class 版本可能有不同的解决方案。
#include <some_c_lib>
void setup(){
//some setup code
//init function from the C library
//with C-style return code for error handling
if(!init()){
//error: program should terminate
//because error cannot be handled
}
//some setup code
}
class myclass{
//some members
public:
myclass(){
//some construction code
//create function of the C library
//with C-style return code error handling
if(!create()){
//error: program should terminate
//because error cannot be handled
}
}
~myclass(){
//desturction code
}
};
int main(){
std::ostream log("log.txt"); //logfile as an example
setup();
myclass obj;
while(everything_is_fine()){
//main loop stuff
}
}
问题是:我不知道终止程序的最佳方法是什么。
我不想在 main
中捕获异常。这会有点无意义和丑陋,因为无论如何都无法处理异常。即便如此,我还是希望有某种堆栈展开机制。如果我只是 exit
if
块中的程序,那么例如日志文件将不会被正确销毁。我对吗?
如果我在 if
中抛出但没有在任何地方提供 try-catch 块,文件会关闭吗?
如何处理构造函数中出现的异常?
是否有处理此类问题的最佳方法?
我希望我的问题是什么变得清楚了。
感谢您的回答,祝您愉快。
一个解决方案是不要以会引发错误的方式编写构造函数。一种常见的做法是使用构造函数来设置成员变量等,然后有一个方法如bool initialize(); return 是一个基于 class 是否可以无误地进行更复杂的初始化的值。
除了布尔值,您还可以 return 其他值或结构以获得更多信息错误。
最后,您的日志文件仍然可以被任何 class 写入,并且应该包含有关错误的信息,如果有的话。
这要看情况,你确实没有提供足够的信息。一般来说,没有绝对的"best"——这取决于你的程序的需要,而不是有"one size fits all"的方法。
只有在琐碎的小程序中(例如,您将在 class 练习中而不是在工作场所中做的事情),错误总是需要立即终止程序。现实世界的需求比这更模糊——取决于你的程序做什么,通常有一个选项可以从错误中恢复并继续(正常情况下,或者在某种降级模式下)。采取措施防止错误(例如,检测错误数据,并在对错误数据执行操作并导致错误情况之前执行某些操作以进行恢复)可能更可取。
不过,一般来说,如果构造函数发生错误(这是不可避免的,一旦发生构造函数无法恢复,等等),则需要抛出异常。这基本上表明调用者(或调用堆栈中的某个函数)需要采取恢复操作。如果调用者无法恢复,则抛出异常的默认结果是程序终止 - 在调用调用堆栈中本地创建的所有对象(具有自动存储持续时间)的析构函数之后。这将终止程序 - 如果有必要 - 并在过程中进行清理,只要析构函数正确清理(这是析构函数的目的)。
在您的代码中,抛出异常将(最终)return 控制到 main()
。如果未捕获异常,程序将终止 - 但不会在 log
被销毁之前终止 - 这会调用其析构函数。标准输出流的析构函数 classes 通常会刷新流并正确关闭它。如果您需要做的不止于此(例如,终止前的其他恢复操作,刷新流后)将 main()
写为函数尝试块。
通常不建议在构造函数中执行 "partial construction" - 例如,构造函数设置一些基础知识,但用户必须调用另一个函数来执行 "further" 初始化。这种技术是忘记进行初始化的机会——这基本上意味着后续代码将使用未正确初始化的对象。在 C++ 中,无论如何都很少需要此类技术 - 可以推迟创建对象,直到所有信息都可用于正确初始化它(在构造函数中)。
一般来说,returning 错误代码(例如具有非 void
return 类型的函数,接受 pointer/reference 到对象的函数存储状态信息)适用于不同的情况。没有任何东西可以强制调用者检查函数中的 return 值。因此,如果可以安全地忽略错误条件(例如,如果您的代码忘记检查它)或者如果该函数仅在将检查 return 代码的情况下使用,则 return 代码是合适的。没有什么能阻止您编写将 return 代码(例如,从用 C 编写的函数)转换为异常的代码。 return 代码的问题是可能会忘记检查它们 - 这可能意味着严重错误仍然存在 undetected/unreported,并导致程序中其他代码出现错误。
所以,基本上我有一个外部 C 库的简单包装代码,我是正确异常处理方面的新手。
提前说明:代码两次显示相同的问题,但 class 版本可能有不同的解决方案。
#include <some_c_lib>
void setup(){
//some setup code
//init function from the C library
//with C-style return code for error handling
if(!init()){
//error: program should terminate
//because error cannot be handled
}
//some setup code
}
class myclass{
//some members
public:
myclass(){
//some construction code
//create function of the C library
//with C-style return code error handling
if(!create()){
//error: program should terminate
//because error cannot be handled
}
}
~myclass(){
//desturction code
}
};
int main(){
std::ostream log("log.txt"); //logfile as an example
setup();
myclass obj;
while(everything_is_fine()){
//main loop stuff
}
}
问题是:我不知道终止程序的最佳方法是什么。
我不想在 main
中捕获异常。这会有点无意义和丑陋,因为无论如何都无法处理异常。即便如此,我还是希望有某种堆栈展开机制。如果我只是 exit
if
块中的程序,那么例如日志文件将不会被正确销毁。我对吗?
如果我在
if
中抛出但没有在任何地方提供 try-catch 块,文件会关闭吗?如何处理构造函数中出现的异常?
是否有处理此类问题的最佳方法?
我希望我的问题是什么变得清楚了。
感谢您的回答,祝您愉快。
一个解决方案是不要以会引发错误的方式编写构造函数。一种常见的做法是使用构造函数来设置成员变量等,然后有一个方法如bool initialize(); return 是一个基于 class 是否可以无误地进行更复杂的初始化的值。
除了布尔值,您还可以 return 其他值或结构以获得更多信息错误。
最后,您的日志文件仍然可以被任何 class 写入,并且应该包含有关错误的信息,如果有的话。
这要看情况,你确实没有提供足够的信息。一般来说,没有绝对的"best"——这取决于你的程序的需要,而不是有"one size fits all"的方法。
只有在琐碎的小程序中(例如,您将在 class 练习中而不是在工作场所中做的事情),错误总是需要立即终止程序。现实世界的需求比这更模糊——取决于你的程序做什么,通常有一个选项可以从错误中恢复并继续(正常情况下,或者在某种降级模式下)。采取措施防止错误(例如,检测错误数据,并在对错误数据执行操作并导致错误情况之前执行某些操作以进行恢复)可能更可取。
不过,一般来说,如果构造函数发生错误(这是不可避免的,一旦发生构造函数无法恢复,等等),则需要抛出异常。这基本上表明调用者(或调用堆栈中的某个函数)需要采取恢复操作。如果调用者无法恢复,则抛出异常的默认结果是程序终止 - 在调用调用堆栈中本地创建的所有对象(具有自动存储持续时间)的析构函数之后。这将终止程序 - 如果有必要 - 并在过程中进行清理,只要析构函数正确清理(这是析构函数的目的)。
在您的代码中,抛出异常将(最终)return 控制到 main()
。如果未捕获异常,程序将终止 - 但不会在 log
被销毁之前终止 - 这会调用其析构函数。标准输出流的析构函数 classes 通常会刷新流并正确关闭它。如果您需要做的不止于此(例如,终止前的其他恢复操作,刷新流后)将 main()
写为函数尝试块。
通常不建议在构造函数中执行 "partial construction" - 例如,构造函数设置一些基础知识,但用户必须调用另一个函数来执行 "further" 初始化。这种技术是忘记进行初始化的机会——这基本上意味着后续代码将使用未正确初始化的对象。在 C++ 中,无论如何都很少需要此类技术 - 可以推迟创建对象,直到所有信息都可用于正确初始化它(在构造函数中)。
一般来说,returning 错误代码(例如具有非 void
return 类型的函数,接受 pointer/reference 到对象的函数存储状态信息)适用于不同的情况。没有任何东西可以强制调用者检查函数中的 return 值。因此,如果可以安全地忽略错误条件(例如,如果您的代码忘记检查它)或者如果该函数仅在将检查 return 代码的情况下使用,则 return 代码是合适的。没有什么能阻止您编写将 return 代码(例如,从用 C 编写的函数)转换为异常的代码。 return 代码的问题是可能会忘记检查它们 - 这可能意味着严重错误仍然存在 undetected/unreported,并导致程序中其他代码出现错误。