为什么C ++的例外不提供调用细节?

时间:2017-12-27 07:36:31

标签: c++

#include <fstream>
#include <iostream>
#include <map>

int main(int argc, char** argv) {
    try {
        std::map<std::string, int> m{{"a", 1}, {"b", 2}};
        std::cout << m.at("c") << std::endl;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }

    return 0;
}

在C ++中,当检索地图的不存在的键时,异常看起来像map::at: key not found。未提供有关的信息。

此外,如果正在访问不存在的文件,则std::ios_base::failure的异常消息看起来像ios_base::clear: unspecified iostream_category error。未提供导致异常的文件名。因此,如果项目中有许多map.at()ifstream is用途,可能需要花费很长时间才能找出异常的来源。

与此相反,Python可能会告诉您KeyError: 'c'FileNotFoundError: [Errno 2] No such file or directory: 'foo'

这只是一个C ++约定吗?谢谢。

3 个答案:

答案 0 :(得分:37)

问题是C ++对象模型,它与Python不同。相比之下,首先让我们回答一下:Python在异常对象中存储什么来打印密钥?它是保持该对象存活的参考。

这不能简单地在C ++中完成。

  1. std::out_of_range无法按原样存储指针或引用。异常的处理程序可能在远处的块中。这意味着在输入处理程序之前,键很可能超出范围。如果引用了密钥,我们会得到未定义的行为。

  2. std::out_of_range是一个具体的类。不是像std::map这样的模板。它不能轻易地将密钥复制到自身中。有许多不同的Key类型,显然无法解释所有这些类型。即使它可以,如果复制密钥非常昂贵怎么办?甚至是不可复制的?即使在这种情况不正确的情况下,如果密钥不能转换为字符串或可打印怎么办?

  3. 以上几点并不意味着它是不可能的。 std::map::at可以抛出一个类型擦除的std::out_of_range子类,依此类推。但我希望你看到它有非平凡的开销。 C ++完全不是为您不需要或使用的功能付费。让每个人无条件地承担这种开销并不符合这种设计。

答案 1 :(得分:3)

C ++标准指定map::at(const key_type& k)将启动std::out_of_range异常(如果值不在其中);仅此而已...但不能少; map::at()的一个特定实现,但问题是k的key_type必须转换为char *。 所以有几种选择:

  1. 不要显示此信息
  2. std::map不存储没有(至少是隐式)转换为char *的类型:但是有一个要求应用于key_store类型并非严格必要
  3. 根据key_type在std::out_of_range例外中提供不同的消息:但此解决方案并不总是显示预期的信息
  4. 从另一个角度来看,std::out_of_range继承自std::logic_error; C ++标准区分了两种主要类型的异常:

    • logic_error:它们是由于内部逻辑错误造成的 程序。从理论上讲,它们是可以预防的。
    • runtime_error:它们是由超出程序范围的事件引起的。它们不能提前预测。

    在我们的例子中,我们可以很容易地检查一个元素的存在(所以我们的案例修复了这个案例)

答案 2 :(得分:0)

With C++, it is possible to create a personalized exception inheriting from std::exception with extra info, like filename. Here is an example :

#include <sstream>

class MyException : public std::exception {
  public :
  MyException (std::exception e, std::string filename = "") : std::exception (e), str_ (filename) {}
  MyException (const MyException& e) : std::exception (e), str_ (e.str_) {}
  const char* what () const throw () {
    std::ostringstream oss;
    oss << "exception occured in " << str_ << " : ";
    oss << std::exception::what ();
    return oss.str ().c_str ();
  }
  std::string str_;
};

int main(int argc, char** argv) {
    try {
      try {
          std::map<std::string, int> m{{"a", 1}, {"b", 2}};
          std::cout << m.at("c") << std::endl;
      }
      catch (const std::exception& e) {
          MyException f (e, "main");
          throw (f);
      }
    }
    catch (const MyException& e) {
      std::cerr << e.what() << std::endl;
    }

    return 0;
}

will display :

exception occured in main : std::exception