82

当我的 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;
    }
}
4

7 回答 7

102

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

boost 有一个关于异常和错误处理良好风格的优秀文档。值得一读。

于 2008-09-25T17:16:46.230 回答
66

几个原则:

  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;
  }
}
于 2008-09-25T17:12:37.020 回答
25

所有这些工作:

#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。请注意,在最不受欢迎的选项中,您需要显式释放内存。

于 2008-09-25T17:14:32.117 回答
8

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

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

于 2008-09-25T17:09:32.677 回答
8

除了可能抛出从 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

于 2008-09-25T18:48:34.810 回答
5

在 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.

于 2013-07-11T18:20:06.370 回答
1

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

使用std::nested_exceptionstd::throw_with_nested

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

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

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

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"
于 2017-12-03T13:52:10.463 回答