【问题标题】:Try-catch-finally from java to c++Try-catch-finally 从 java 到 c++
【发布时间】:2018-01-29 11:05:01
【问题描述】:

我想翻译c++中的java异常处理,不使用RAII。

问题主要是finally块。

我找到了一篇使用以下方法的论文:

“一个困难是 finally 子句必须在每次退出 try 块之前执行。一般的 try 块有惊人数量的潜在退出:正常退出、抛出异常、返回 语句、break 语句、 continue 语句或未能捕获异常。 finally 子句必须在每一个之前执行(如果它们在 try 块或 catch 块中使用)。例如,下面的 Java 代码展示了大部分退出 try 块的方法:

try {
  switch (x) {
    case 0: continue next;
    case 1: break out;
    case 2: throw new TException();
    case 3: return(0);
  }
} catch (Error e) {
    throw(e);
} finally {
    System.out.println("finally");
}

我们的解决方案是在所有必须执行的地方简单地复制 finally 块的文本。如果 finally 块的大小很大,这可能表明 Java 程序员可能希望将其设为方法,以尽量减少 C++ 中的代码重复。

以下 C++ 代码显示了如何为 try 块的所有退出复制 finally 块:"

  try
   {
     switch(x)
     {
     case 0:
     /* finally clause, continue from try block */
     java_lang_System::out->println(_j_toString("finally"));
     goto next;
     case 1:
     /* finally clause, break from try block */
     java_lang_System::out->println(_j_toString("finally"));
     goto out;
     case 2:
     /* finally clause, throw exception from try block */
     java_lang_System::out->println(_j_toString("finally"));
     throw(new TException());
     case 3:
     /* finally clause, return from try block */
     java_lang_System::out->println(_j_toString("finally"));
     return(0);
     }
   }
   catch(java_lang_Error *e)
   {
     /* finally clause, caught exception from try block */
     java_lang_System::out->println(_j_toString("finally"));
     throw(e);
   }
   catch(...)
   {
     /* finally clause, uncaught exception from try block */
     java_lang_System::out->println(_j_toString("finally"));
     throw;
   }
     /* finally clause, normal exit from try block */
     java_lang_System::out->println(_j_toString("finally"));

我不明白如何在真正的通用程序示例中使用它。 switch中的变量x是什么,应该如何使用?

【问题讨论】:

  • “我想说英语,不使用一般现在时。”
  • "...不使用 RAII" 为什么? Java 最终需要,因为它确实没有具有 RAII。
  • 我想翻译c++中的java异常处理,不使用RAII问题从这里开始。
  • 转到下一个;你是认真的吗?

标签: java c++ exception-handling switch-statement try-catch-finally


【解决方案1】:

GSL 是一个支持CppCoreGuidelines 的库。指南建议为此使用gsl::finally

#include <gsl/gsl_util>

int main()
{
    std::srand(unsigned(std::time(nullptr)));

    auto cleanup = gsl::finally([]{
        std::cout << "FINALLY it is my turn." << '\n';
    });

    try
    {
        if(std::rand() % 2)
            throw std::runtime_error("Woopsie 1");

        if(std::rand() % 2)
            throw std::logic_error("Woopsie 2");

        std::cout << "No woopsies on me" << '\n';
    }
    catch(std::runtime_error const& e)
    {
        std::cout << e.what() << '\n';
    }
    catch(std::logic_error const& e)
    {
        std::cout << e.what() << '\n';
    }
}

但你真的应该为新课程改用RAII 设计。这样就不需要使用“finally”对象了。

注意:请在现实生活中从#include &lt;random&gt;获取随机数。

【讨论】:

  • 但那是 RAII ;)
  • @StoryTeller 哈!我试图保守这个秘密!嗯,它是一种外部 RAII 对象,可以在所有非 RAII 对象之后进行清理。最好的解决方案是让你的所有对象RAII
  • 指定的 RAII 类要好得多,我同意 100%。虽然我觉得如果 p0577r0 被 C++2a 接受,我们将会看到更多使用 gsl::finally
【解决方案2】:

这只是示例代码。重点是,java finally 块实际上是在多个点插入的。显示的代码只是一个示例,列出了需要复制 finally 块的代码的 breakcontinuereturnthrow - 作为 final 代码。

例如:

try {
    ...
    return ...;
    ...
} finally {
    A;
}

会变成

try {
    ...
    { A; }
    return ...;
    ...
    { A; }
} catch (java_lang_Error *e) {
    A;
    throw e; // If the catch was not there originally
}

其实这就是java字节码中发生的事情。

所以所有离开java try-finally的代码都需要复制finally块的代码。

【讨论】:

  • 我不确定我是否跟随你的转变。如果在第一个 { A; } 之前抛出异常,它将不会执行第二个插入的块。所以它在语义上并不是真正等价的
  • @StoryTeller 谢谢,我忘了添加catch 作为控制流元素。事实上,在原始代码中不需要有任何问题。 Galik 的回答更好地处理了可能的异常; java_lang_Error 没有意义。但是我想告诉您,显然 OP 并未将代码视为示例代码。
猜你喜欢
  • 2011-10-31
  • 2011-10-26
  • 1970-01-01
  • 2011-11-17
  • 2011-06-01
  • 2015-09-05
  • 2014-11-27
  • 2016-03-28
  • 1970-01-01
相关资源
最近更新 更多