【问题标题】:c++ exception : throwing std::stringc++ 异常:抛出 std::string
【发布时间】:2010-09-13 04:05:10
【问题描述】:

当我的 C++ 方法遇到奇怪且无法恢复时,我想抛出异常。可以抛出std::string指针吗?

这是我期待做的事情:

void Foo::Bar() {
    if(!QueryPerformanceTimer(&m_baz)) {
        throw new std::string("it's the end of the world!");
    }
}

void Foo::Caller() {
    try {
        this->Bar(); // should throw
    }
    catch(std::string *caught) { // not quite sure the syntax is OK here...
        std::cout << "Got " << caught << std::endl;
    }
}

【问题讨论】:

  • 这是合法的,但不道德。
  • 您有内存泄漏。谁在删除抛出的字符串指针?不要对异常使用指针。
  • 我知道有点晚了,但不管怎样,这篇文章在这个问题上有很多点boost.org/community/error_handling.html

标签: c++ exception stl


【解决方案1】:

是的。 std::exception 是 C++ 标准库中的基本异常类。您可能希望避免将字符串用作异常类,因为它们本身可以在使用过程中引发异常。如果发生这种情况,那你会在哪里?

boost 在异常和错误处理方面具有出色的 document 风格。值得一读。

【讨论】:

  • 旁注:如果异常对象本身抛出,std::terminate 将被调用,这就是你的位置(它不漂亮!)
  • 请参阅gotw.ca/publications/mill16.htm 以了解为什么处理分配抛出异常是浪费时间的一个论点。这个答案的另一个论点是 std::runtime_exception 和家人做到了,那你为什么不呢?
【解决方案2】:

几个原则:

  1. 你有一个 std::exception 基类,你应该让你的异常派生自它。这样一般的异常处理程序仍然有一些信息。

  2. 不要抛出指针而是对象,这样会为你处理内存。

例子:

struct MyException : public std::exception
{
   std::string s;
   MyException(std::string ss) : s(ss) {}
   ~MyException() throw () {} // Updated
   const char* what() const throw() { return s.c_str(); }
};

然后在你的代码中使用它:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw MyException("it's the end of the world!");
  }
}

void Foo::Caller(){
  try{
    this->Bar();// should throw
  }catch(MyException& caught){
    std::cout<<"Got "<<caught.what()<<std::endl;
  }
}

【讨论】:

  • 从 std::runtime_exception 派生不是更好吗?
  • 请注意,christopher_f 的论点仍然有效:您的异常可能会在构造时引发异常...值得深思,我想... :-D ...我可能是错的,但例外是应该通过他们的 const-reference 来捕获,不是吗?
  • 对于 const-reference,它是可能的,但不是强制性的。我想了一会儿……没有找到任何支持或反对它的参考资料。
  • 这里的 const ref 仅有用,因此您不会意外修改 catch 块中的异常。无论如何你都不会这样做,所以只需通过 nonconst ref 捕捉即可
  • 我试了这段代码,有编译错误,关于析构方法的问题...
【解决方案3】:

所有这些工作:

#include <iostream>
using namespace std;

//Good, because manual memory management isn't needed and this uses
//less heap memory (or no heap memory) so this is safer if
//used in a low memory situation
void f() { throw string("foo"); }

//Valid, but avoid manual memory management if there's no reason to use it
void g() { throw new string("foo"); }

//Best.  Just a pointer to a string literal, so no allocation is needed,
//saving on cleanup, and removing a chance for an allocation to fail.
void h() { throw "foo"; }

int main() {
  try { f(); } catch (string s) { cout << s << endl; }
  try { g(); } catch (string* s) { cout << *s << endl; delete s; }
  try { h(); } catch (const char* s) { cout << s << endl; }
  return 0;
}

你应该更喜欢 h 而不是 f 而不是 g。请注意,在最不推荐的选项中,您需要显式释放内存。

【讨论】:

  • 但是将const char 指向局部变量的指针不是错误吗?是的,我当然知道编译器会将 c 字符串放在未修改的部分中,在应用程序运行之前不会更改地址。但它是未定义的;此外,如果这段代码在一个库中,在抛出错误后就消失了,那会怎样?顺便说一句,我在我的项目中也做了很多这样的坏事,我只是一个学生。但我应该考虑一下...
  • @Hi-Angel 没有局部变量;抛出的是一个字符串文字,它在生命周期方面具有标准的具体和明确定义的处理,您的担忧是没有实际意义的。参见例如stackoverflow.com/a/32872550/2757035 如果这里有问题,基本上不会丢消息(至少在不需要过度的额外杂技/风险的情况下不会),所以当然没有,没关系。
【解决方案4】:

它有效,但如果我是你,我不会这样做。完成后,您似乎并没有删除该堆数据,这意味着您已经创建了内存泄漏。 C++ 编译器负责确保异常数据即使在堆栈被弹出时也保持活动状态,所以不要觉得您需要使用堆。

顺便说一句,抛出std::string 并不是最好的方法。如果您使用简单的包装器对象,您将拥有更多的灵活性。它现在可能只是封装了一个string,但也许将来你会想要包含其他信息,比如一些导致异常的数据或者一个行号(非常常见)。您不想更改代码库中每个位置的所有异常处理,所以现在走上正轨,不要抛出原始对象。

【讨论】:

    【解决方案5】:

    除了可能抛出从 std::exception 派生的东西之外,您还应该抛出匿名临时对象并通过引用捕获:

    void Foo::Bar(){
      if(!QueryPerformanceTimer(&m_baz)){
        throw std::string("it's the end of the world!");
      }
    }
    
    void Foo:Caller(){
      try{
        this->Bar();// should throw
      }catch(std::string& caught){ // not quite sure the syntax is ok here...
        std::cout<<"Got "<<caught<<std::endl;
      }
    }
    
    • 你应该匿名 临时的,所以编译器处理 与任何对象的生命周期 你在扔 - 如果你扔 一些新的东西, 其他人需要释放 东西。
    • 您应该捕获对 防止对象切片

    .

    详情请参阅 Meyer 的“Effective C++ - 第 3 版”或访问 https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+reference

    【讨论】:

      【解决方案6】:

      在 C++ 中抛出异常的最简单方法:

      #include <iostream>
      using namespace std;
      void purturb(){
          throw "Cannot purturb at this time.";
      }
      int main() {
          try{
              purturb();
          }
          catch(const char* msg){
              cout << "We caught a message: " << msg << endl;
          }
          cout << "done";
          return 0;
      }
      

      打印出来:

      We caught a message: Cannot purturb at this time.
      done
      

      如果你捕捉到抛出的异常,异常就会被包含,程序将继续运行。如果你没有捕捉到异常,那么程序存在并打印:

      This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.

      【讨论】:

      • 这似乎是个坏主意 - catch (std::exception&amp;) 不会明白的。
      【解决方案7】:

      虽然这个问题已经很老了并且已经得到解答,但我只想添加一个注释,说明如何在 C++11 中进行正确的异常处理:

      使用std::nested_exceptionstd::throw_with_nested

      在我看来,使用这些会导致更清晰的异常设计,并且无需创建异常类层次结构。

      请注意,这使您能够在代码中获取异常的回溯,而无需调试器或繁琐的日志记录。在 StackOverflow herehere 上描述了如何编写一个适当的异常处理程序来重新抛出嵌套异常。

      由于您可以对任何派生的异常类执行此操作,因此您可以向此类回溯添加大量信息! 你也可以看看我的MWE on GitHub,回溯看起来像这样:

      Library API: Exception caught in function 'api_function'
      Backtrace:
      ~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
      ~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-25
        • 2012-04-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多