我有一个类,称之为A
,其构造函数接受一些输入参数,如果它们与构造该对象不兼容,则可能抛出异常。在我的main
代码中,我构造了一个A
类型的对象,如下所示:
A my_obj(arg1,arg2,arg3);
并使用它。显然,如果构造函数失败并抛出异常,则在打印出“未处理的异常”消息后,程序的执行将被终止。
但是,我想在这种情况下向用户提供更多信息,并告诉他/她为什么抛出了exception
。所以,我需要一种方法来catch
例外。
为此,一种可能性是封装整个代码,从my_obj
的声明开始直到try
块中的程序结束,然后catch
异常:
try {
A my_obj(arg1, arg2, arg3);
// ...
// about 100 other lines of code being executed if my_obj is created properly
}
catch (std::exception& e) {
// print a user-friendly error message and exit
}
但这对我来说有点“矫枉过正”。特别是因为在剩余的100行中没有抛出其他异常。有没有其他更好的方法来实现这一目标?
答案 0 :(得分:2)
如果构造函数抛出,则表示没有对象。 std::optional<>
是一种意味着&#34;我们这里可能没有对象的类型&#34;。
template <typename T, typename ... Args>
std::optional<T> try_make(Args&& ... args)
{ try {
return make_optional(std::forward(args...));
} catch (...) {
return {};
} }
然后
auto my_obj = try_make<A>(arg1,arg2,arg3);
if (my_obj) {
// about 100 other lines of code being executed if my_obj is created properly
}
答案 1 :(得分:1)
一种可能性是使用指针(更好地使用智能指针,如下面的代码中的unique_ptr)。您可以将String.format()
留空,在try块中调用构造函数并将指针移动到unique_ptr
。之后,您的其他代码将执行。当然,您必须在简单的if语句中检查operator bool unique_ptr
的有效指针。
为简化unique_ptr
的使用,我们会引用my_obj
。
A& my_obj_ref = *my_obj;
请记住,这种方式会将您的对象分配到堆而不是堆栈。
答案 2 :(得分:0)
您可以将对象构造抽象为捕获异常的函数:
declare
cursor c1 is
select 'R' PRD_ID, 'A' COMP, 'Y' TD1, 'Y' TD2, 'Y' TD3 from dual;
cursor c2 is
select 'S' ACC_ID, 'A' COMP, 'Y' TD1, 'Y' TD2, 'Y' TD3 from dual;
c1_row c1%rowtype;
c2_row c2%rowtype;
FUNCTION test_equal (c1_row c1%rowtype, c2_row c1%rowtype) RETURN BOOLEAN
IS
BEGIN
IF c1_row.td1 = c2_row.td1
AND c1_row.td2 = c2_row.td2
AND c1_row.td3 = c2_row.td3
THEN RETURN TRUE;
ELSE RETURN FALSE;
END IF;
END;
begin
open c1;
open c2;
loop
fetch c1 into c1_row;
exit when c1%notfound;
fetch c2 into c2_row;
exit when c2%notfound;
if test_equal(c1_row, c2_row)
then
-- do something
end if;
end loop;
end;
如果构造失败,上面会使用您的程序退出的事实。如果要求继续运行,该函数可以返回template<typename... Args>
A make_a(Args&&... args) {
try {
return A(std::forward(args)...);
}
catch (std::exception& e) {
// print a user-friendly error message and exit
...
std::exit(EXIT_FAILURE);
}
}
// ... in the actual code:
A my_obj = make_a(arg1, arg2, arg3);
(如果您无法访问C ++ 17,则可以返回其等效值。)
答案 3 :(得分:0)
这里有几个选项,具体取决于如果构造失败,您希望控制如何继续。
如果你想通过抛出异常退出函数,那么你不需要做任何事情,你可以让A
构造异常传播开来。
如果要通过抛出不同的异常退出,或者在让A
构造异常传播之前执行某些操作,则使用执行这些操作的工厂函数(可能是lambda),例如:< / p>
auto a_factory(T x, U y) -> A // or use perfect forwarding
{
try { return A(x, y); }
catch(...) {
log("constructing A failed...");
throw other_exception();
}
}
// ...
A my_obj = a_factory(x, y);
如果要通过返回值退出,则仍然可以使用上述方法,但将调用函数包装在另一个捕获预期异常的函数中并返回一个值。
或者您可以使用optional
(下方)或unique_ptr
(由其他答案涵盖)技术,但可以从return
块执行catch
语句。< / p>
如果你想在没有有效A
的情况下继续执行,那么你可以这样做:
std::optional<A> opt_my_obj;
try
{
A temp(...args...);
opt_my_obj.swap(temp);
} catch(...)
{
// handling, you could return from the function here
}
// At this point you can test `if ( opt_my_obj )` to branch the flow.
// When you're at a point where you have verified the object exists, you
// can enable normal object syntax by writing:
A& my_obj = *opt_my_obj;
如果您的函数中有多个对象需要考虑这些问题,我倾向于建议将整个函数包装在try ... catch中的版本可以处理所有不同的异常。
答案 4 :(得分:0)
我倾向于这样做简单:抛出人类可读的消息。当没有选择时,这种策略很有效,而且通常没有。虽然有一个问题,你希望异常处理相当健壮,所以我将消息打包在std::array<char,4096>
必要时截断并记住零终止符(我知道这可能会破坏堆栈但它应该没问题如果我们不在递归函数中),那就扔掉它。
示例:
try
{
Options opts(argv);
SomeResource resource(opts.someParameter());
//...More actions that could throw
}
catch(const std::array<char,4096>& errmessage) //Or rather some other type that contains the message.
{
fprintf(stderr,"Error: %s\n",errmessage.data());
return -1; //Or any non-zero value
}
return 0;
优点:
缺点:
缺乏上下文:消息必须说“不可能打开文件foo:没有这样的文件或目录。”。不告诉用户异常的根本原因。此问题继承自异常模型,如果不将异常视为美化错误代码,则无法解决此问题
如果要对异常内容进行分支,则必须解析该消息,但我发现这很少需要。可能在编译器的上下文中,但无论如何都会打印该消息{.1}}。