Catch derived exception when returning reference of base class type?
I'm writing a windows application in C++ and encountered the following problem when working with exceptions.
I have a base exception class from which all other exceptions derive from. In the base class I have a method for the error message of any exception. That method then returns the exception (through '*this').
Now, the problem occurs when I want to extend a derived exception and later user it in a catch block. Since the extend method is declared in the base class, the catch block catches the base class instead of the derived class. Is there any way of working around this so that the correct derived class is caught instead?
Here are some code illustrating the problem:
// DECLARATIONS
class BaseException {
BaseException() { }
Exception& extend( string message ) {
// extend message
return *this;
}
}
class DerivedException : public BaseException {
DerivedException() : Exception() { }
}
// RUNNING CODE
int main() {
try {
...
try {
...
// Something goes wrong
throw DerivedException( "message1" );
}
catch ( DerivedException& exc ) {
throw exc.extend( "message2" );
}
}
catch ( DerivedException& ) {
// Here is where I *want* to land开发者_运维知识库
}
}
catch ( BaseException& ) {
// Here is where I *do* land
}
}
At the moment I have "solved" it by not making the extend method virtual, but declaring it in each exception with correct return type. It works, but it's not pretty.
It would be much simpler to separate the extend()
call and the re-throwing of the exception:
catch ( DerivedException& exc ) {
exc.extend( "message2" );
throw;
}
This way extend()
doesn't have to return anything and always the right exceptions are thrown/caught.
This can be done using the "Curiously recurring template pattern"
template <typename TExc>
class BaseException {
BaseException() { }
TExc& extend( string message ) {
// extend message
return (TExc) *this;
}
}
class DerivedException : public BaseException<DerivedException> {
DerivedException() : BaseException() { }
}
The problem is that return type of extend is 'BaseException&'. Therefore 'throw exc.extend()' is caught in the wrong exception handler.
If you want to throw the same exception, use can use the empty throw; statement. The OP code had lots of errors.
class BaseException {
public:
BaseException() { }
BaseException& extend( string message ) {
// extend message
return *this;
}
};
class DerivedException : public BaseException {
public:
DerivedException() : BaseException() { }
};
// RUNNING CODE
int main() {
try {
try {
// Something goes wrong
throw DerivedException();
}
catch ( DerivedException& exc ) {
throw exc.extend("A");
}
}
catch ( DerivedException& ) {
// Here is where I *want* to land
int i = 0;
}
catch ( BaseException& ) {
// Here is where I *do* land
int i = 0;
}
}
You should make the extend function virtual, and return a DerivedException& in the DerivedException's override. This is legal only for special cases where the return value can be implicitly casted - such as where the original function signature returns a base class of the override function signature's return value, as I show below.
class BaseException {
public:
BaseException() { }
virtual BaseException& extend( std::string message ) {
// extend message
return *this;
}
};
class DerivedException : public BaseException {
public:
virtual DerivedException& extend( std::string message ) {
return *this;
}
DerivedException(std::string) : BaseException() { }
};
// RUNNING CODE
int main() {
try {
try {
// Something goes wrong
throw DerivedException( "message1" );
}
catch ( DerivedException& exc ) {
throw exc.extend( "message2" );
}
}
catch ( DerivedException& ) {
std::cout << "DerivedException!";
}
catch ( BaseException& ) {
std::cout << "BaseException!";
}
std::cin.get();
}
Displays DerivedException!
精彩评论