【问题标题】:why doesn't catch block share the scope of the try block?为什么 catch 块不共享 try 块的范围?
【发布时间】:2011-07-25 11:45:23
【问题描述】:

令人惊讶的是,我无法通过谷歌搜索和搜索 SO 找到答案(关于 SO 的类似问题很多,但与其他语言相关)。

我怀疑答案是no。如果是这样,就会有明显的不便,例如

try
{
  std::string fname = constructFileName(); // can throw MyException
  ofstream f;
  f.exceptions(ofstream::failbit | ofstream::badbit);
  f.open(fname.c_str());
  // ...
}
catch (ofstream::failure &e)
{
  cout << "opening file " << fname << " failed\n"; // fname is not in the scope
}
catch (MyException &e)
{
  cout << "constructing file name failed\n";
}

如果我的假设是正确的,你会如何处理?通过将std::string fname; 移出try,我猜?

我知道范围是由 {} 块定义的,但这似乎是一个合理的情况,嗯,例外。是不是抛出异常导致对象不能完全构造的原因?

【问题讨论】:

  • 就在现场。如果在try { A a; B b; }A 的构造函数抛出,ab 都不能合理地在范围内。
  • 除此之外,编译器可能不知道constructFileName() 是否可以抛出ofstream::failure。如果fname 仍然在第一个catch 块的范围内,请考虑何时将其破坏- 在catch 执行之后,但有条件地取决于抛出的来源。呸。您可以尝试告诉编译器每个函数抛出什么,但我不太喜欢对象的生命周期和范围取决于constructFileName() 的异常规范。销毁在投掷点之前创建的所有内容要干净得多,然后处理 catch 子句。

标签: c++ exception-handling error-handling try-catch


【解决方案1】:

它们超出了范围。这是因为如果你声明一个对象并尝试初始化它,而你初始化它的方式会抛出异常,它会被捕获。

【讨论】:

    【解决方案2】:

    虽然 James 的帖子正确回答了您的问题,但它没有提供通常的解决方法:swap。假设 constructFileName() 返回 std::string 而不是 char const*,以下是惯用的:

    std::string fname;
    try
    {
        constructFileName().swap(fname); // can throw MyException
        std::ofstream f;
        f.exceptions(std::ios_base::failbit | std::ios_base::badbit);
        f.open(fname.c_str());
        // ...
    }
    catch (std::ios_base::failure &e)
    {
        std::cout << "opening file " << fname << " failed\n";
    }
    catch (MyException &e)
    {
        std::cout << "constructing file name failed\n";
    }
    

    【讨论】:

    • 谢谢。为什么swap 在这里比分配更好?如果在赋值期间抛出异常,这是否会使fname 处于未定义状态?
    • @davka :使用 C++0x 之前的编译器,operator= 将导致在赋值期间存在两个数据副本,这非常且完全不必要地低效,而使用 swap最多会有一份数据副本。使用 C++0x 编译器,在这种情况下,任何一种方式都无关紧要,因为 constructFileName 的结果将是一个右值并因此移动到 fname (同时确保最多有一个数据副本) .简而言之:swap 在除尖端编译器之外的所有编译器上效率更高。
    【解决方案3】:

    有一个明显的原因:您不能信任在 try 块中创建的对象的状态。那里的代码被异常中断了,它们的构造函数可能还没有运行。

    【讨论】:

    • 我认为这并不明显。例如在也有构造函数的 python 中,try 块没有定义新的范围。
    • @Étienne 这不是一个合适的比较。在 Python 中,您引用的变量是在运行时动态确定的。因此,如果不构造变量,则该名称甚至都不存在。在 C++ 中,情况并非如此。
    【解决方案4】:

    catch 块是否共享 try 块的范围?

    没有。

    你如何处理这个问题?通过将std::string fname; 移出try,我猜?

    是的。

    我知道范围是由 {} 块定义的,但这似乎是一个合理的情况,嗯,例外。是不是抛出异常导致对象不能完全构造的原因?

    C++ 最不需要的就是更复杂的规则和规则例外。 :-)

    【讨论】:

      猜你喜欢
      • 2012-07-24
      • 1970-01-01
      • 2019-08-02
      • 2015-12-05
      • 1970-01-01
      • 2011-01-01
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      相关资源
      最近更新 更多