C ++异常:抛出std :: string


80

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

23
这是合法的,但不是道德的。
Marcin

18
您有内存泄漏。谁在删除抛出的字符串指针?不要为异常使用指针。
fnieto-Fernando Nieto

2
我知道有些晚了,但是无论如何,这篇文章在这个问题上有很多要点boost.org/community/error_handling.html
Alex

Answers:


100

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

升压拥有出色的文档良好的作风为异常和错误处理。值得一读。


20
旁注:如果异常对象本身抛出,则将调用std :: terminate,这将是您要去的地方(而且它并不漂亮!)
Alaric

6
有关为什么为什么要烦恼分配引发异常浪费时间的说法,请参阅gotw.ca/publications/mill16.htm。关于这个答案的另一个论点是std :: runtime_exception和family做到了,那为什么不呢?
格雷格·罗杰斯

63

一些原则:

  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;
  }
}

5
从std :: runtime_exception派生会更好吗?
马丁·约克

请注意,christopher_f的参数仍然有效:您的异常可能会在构造时引发异常。。。值得深思,我想...:-D ...参考编号?
paercebal

对于const引用,这是可能的,但不是强制性的。我想知道一会儿...没有找到任何支持或反对的参考。
PierreBdR

此处的const ref仅有用,因此您不会意外地在catch块中修改异常。您无论如何都不会这样做,所以请

我尝试了这段代码,出现了编译错误,关于析构函数的方法……
DivyZeroZero 2013年

24

所有这些工作:

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


1
但是,将const char指针指向局部变量不是bug吗?是的,我当然知道编译器会将c字符串放在未修改的部分中,直到应用程序运行后,它才会更改地址。但这是未定义的。此外,如果此代码位于引发错误之后就消失的库中,那将是什么?顺便说一句,我在项目中也做过很多这样的坏事,我只是一个学生。但是我应该考虑一下……
天使

1
@ Hi-Angel没有局部变量;抛出的是一个字符串文字,该字符串在标准的生命周期方面经过了标准的明确定义,并且您的担心是没有根据的。参见例如stackoverflow.com/a/32872550/2757035如果此处存在问题,则基本上不会抛出任何消息(至少在不需要不适当的额外杂技/风险的情况下是不可能的),因此当然没有,也很好。
underscore_d

8

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

顺便说一句,抛出astd::string并不是开始的最佳方法。如果使用简单的包装器对象,那么您将获得更大的灵活性。它可能string暂时只封装了一个,但是将来可能会包含其他信息,例如导致异常的某些数据或行号(非常常见的那个)。您不想在代码库中的每个位置更改所有异常处理,因此,现在就走高路,不要抛出原始对象。


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 ++-第三版”,或访问https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+reference


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.


3
这似乎是个坏主意-catch (std::exception&)不会理解。
Timmmm '16

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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.