使用异常时 C++ 代码变得非线性
C++ code became no-linear when using exception
我的项目主要是这样的
Object a;
if (! a.initialize(x, y, z))
return EXIT_FAILURE;
// 100 lines using a
a.finalize();
我正在尝试更改这部分代码并使用 RAII idiome。
因此,我删除了 initialize
函数和 finalize
并移动了构造函数和析构函数中的代码。
为了捕获 initialize()
错误,如果出现问题,我会在构造函数中抛出异常。
所以现在,我的代码是这样的:
try
{
Object a(x, y, z);
// 100 lines using a
} catch (my_exception&)
{
return EXIT_FAILURE;
}
觉得麻烦的是100行代码。我的 try
太长了,只有一个错误。我有多个对象,例如 a
.
所以在我的代码是线性的之前:
Object a;
if (! a.initialize(x, y, z))
return EXIT_FAILURE;
Object b;
Object c;
if (!b.initialize() || !c.initialize())
return EXIT_FAILURE;
a.finalize();
现在看起来很难看,难以阅读:
try
{
Object a(x, y, z);
try
{
Object b;
try
{
Object c;
}
catch (my_exception_c&)
{
return EXIT_FAILURE;
}
}
catch (my_exception_b&)
{
return EXIT_FAILURE;
}
} catch (my_exception&)
{
return EXIT_FAILURE;
}
如何使用RAII并保持代码清晰?
一般来说,在你想要处理异常的级别创建一个try块。在这种情况下,您只需要一个顶级块来在任何异常之后进行清理:
try {
Object a(x, y, z);
Object b;
Object c;
// code using these
} catch (...) {
// end the program if any exception hasn't been handled
return EXIT_FAILURE;
}
现在不是"too long for one error";对于可能发生的任何错误,它都是正确的长度。
将自己限制在从 std::exception
派生的异常中是个好主意;那么你可以在未处理的情况下提供一些可能有用的信息:
catch (std::exception const & ex) {
std::cerr << "ERROR: " << ex.what() << std::endl;
return EXIT_FAILURE;
}
你只需要一个这样的捕获物:
try
{
Object a;
Object b;
//100 lines of code
}
catch(ExeptionA& exa)
{
cerr << "error a" << endl;
}
catch(ExeptionB& exa)
{
cerr << "error B" << endl;
}
catch(...){
cerr << "some other error" << endl;
}
(“...”的字面意思是“...”)
这样,您将在同一捕获中捕获来自 ObjectA 和 ObjectB 等的所有异常。因此,如果您创建自定义异常,最好在它的来源处添加一些信息。
你总是可以有另一个像 "IsValid()" 这样的函数,你可以在调用构造函数后检查它而不是抛出异常。您保留了 RAII 的好处(异常安全、针对 initialization/destruction 错误的保护,...),但您将能够使您的代码保持与以前相同的格式。
就 C++ 良好实践而言,它不是那么干净和安全,因为用户可能会忘记检查它是否有效,但选项就在那里。
我的项目主要是这样的
Object a;
if (! a.initialize(x, y, z))
return EXIT_FAILURE;
// 100 lines using a
a.finalize();
我正在尝试更改这部分代码并使用 RAII idiome。
因此,我删除了 initialize
函数和 finalize
并移动了构造函数和析构函数中的代码。
为了捕获 initialize()
错误,如果出现问题,我会在构造函数中抛出异常。
所以现在,我的代码是这样的:
try
{
Object a(x, y, z);
// 100 lines using a
} catch (my_exception&)
{
return EXIT_FAILURE;
}
觉得麻烦的是100行代码。我的 try
太长了,只有一个错误。我有多个对象,例如 a
.
所以在我的代码是线性的之前:
Object a;
if (! a.initialize(x, y, z))
return EXIT_FAILURE;
Object b;
Object c;
if (!b.initialize() || !c.initialize())
return EXIT_FAILURE;
a.finalize();
现在看起来很难看,难以阅读:
try
{
Object a(x, y, z);
try
{
Object b;
try
{
Object c;
}
catch (my_exception_c&)
{
return EXIT_FAILURE;
}
}
catch (my_exception_b&)
{
return EXIT_FAILURE;
}
} catch (my_exception&)
{
return EXIT_FAILURE;
}
如何使用RAII并保持代码清晰?
一般来说,在你想要处理异常的级别创建一个try块。在这种情况下,您只需要一个顶级块来在任何异常之后进行清理:
try {
Object a(x, y, z);
Object b;
Object c;
// code using these
} catch (...) {
// end the program if any exception hasn't been handled
return EXIT_FAILURE;
}
现在不是"too long for one error";对于可能发生的任何错误,它都是正确的长度。
将自己限制在从 std::exception
派生的异常中是个好主意;那么你可以在未处理的情况下提供一些可能有用的信息:
catch (std::exception const & ex) {
std::cerr << "ERROR: " << ex.what() << std::endl;
return EXIT_FAILURE;
}
你只需要一个这样的捕获物:
try
{
Object a;
Object b;
//100 lines of code
}
catch(ExeptionA& exa)
{
cerr << "error a" << endl;
}
catch(ExeptionB& exa)
{
cerr << "error B" << endl;
}
catch(...){
cerr << "some other error" << endl;
}
(“...”的字面意思是“...”)
这样,您将在同一捕获中捕获来自 ObjectA 和 ObjectB 等的所有异常。因此,如果您创建自定义异常,最好在它的来源处添加一些信息。
你总是可以有另一个像 "IsValid()" 这样的函数,你可以在调用构造函数后检查它而不是抛出异常。您保留了 RAII 的好处(异常安全、针对 initialization/destruction 错误的保护,...),但您将能够使您的代码保持与以前相同的格式。 就 C++ 良好实践而言,它不是那么干净和安全,因为用户可能会忘记检查它是否有效,但选项就在那里。