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

当我的 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;
}
}
176935 次浏览

It works, but I wouldn't do it if I were you. You don't seem to be deleting that heap data when you're done, which means that you've created a memory leak. The C++ compiler takes care of ensuring that exception data is kept alive even as the stack is popped, so don't feel that you need to use the heap.

Incidentally, throwing a std::string isn't the best approach to begin with. You'll have a lot more flexibility down the road if you use a simple wrapper object. It may just encapsulate a string for now, but maybe in future you will want to include other information, like some data which caused the exception or maybe a line number (very common, that). You don't want to change all of your exception handling in every spot in your code-base, so take the high road now and don't throw raw objects.

A few principles:

  1. you have a std::exception base class, you should have your exceptions derive from it. That way general exception handler still have some information.

  2. Don't throw pointers but object, that way memory is handled for you.

Example:

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

And then use it in your code:

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

All these work:

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

You should prefer h to f to g. Note that in the least preferable option you need to free the memory explicitly.

Yes. std::exception is the base exception class in the C++ standard library. You may want to avoid using strings as exception classes because they themselves can throw an exception during use. If that happens, then where will you be?

boost has an excellent document on good style for exceptions and error handling. It's worth a read.

In addition to probably throwing something derived from std::exception you should throw anonymous temporaries and catch by reference:

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;
}
}
  • You should throw anonymous temporaries so the compiler deals with the object lifetime of whatever you're throwing - if you throw something new-ed off the heap, someone else needs to free the thing.
  • You should catch references to prevent object slicing

.

See Meyer's "Effective C++ - 3rd edition" for details or visit https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+reference

Simplest way to throw an Exception in 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;
}

This prints:

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

If you catch the thrown exception, the exception is contained and the program will ontinue. If you do not catch the exception, then the program exists and prints:

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

Though this question is rather old and has already been answered, I just want to add a note on how to do proper exception handling in C++11:

Use std::nested_exception and std::throw_with_nested

Using these, in my opinion, leads to cleaner exception design and makes it unnecessary to create an exception class hierarchy.

Note that this enables you to get a backtrace on your exceptions inside your code without need for a debugger or cumbersome logging. It is described on StackOverflow here and here, how to write a proper exception handler which will rethrow nested exceptions.

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHub, where a backtrace would look something like this:

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"