Search code examples
c++exceptionrethrow

C++ Exceptions questions on rethrow of original exception


Will the following append() in the catch cause the rethrown exception to see the effect of append() being called?

try {
  mayThrowMyErr();
} catch (myErr &err) {
  err.append("Add to my message here");
  throw; // Does the rethrow exception reflect the call to append()?
}

Similarly, if I rewrite it this way, will bit slicing occur if the actual exception is derived by myErr?

try {
  mayThrowObjectDerivedFromMyErr();
} catch (myErr &err) {
  err.append("Add to my message's base class here");
  throw err; // Do I lose the derived class exception and only get myErr?
}

Solution

  • In both cases, since you catch by reference, you are effectively altering the state of the original exception object (which you can think of as residing in a magical memory location which will stay valid during the subsequent unwinding -- 0x98e7058 in the example below). However,

    1. In the first case, since you rethrow with throw; (which, unlike throw err;, preserves the original exception object, with your modifications, in said "magical location" at 0x98e7058) will reflect the call to append()
    2. In the second case, since you throw something explicitly, a copy of err will be created then thrown anew (at a different "magical location" 0x98e70b0 -- because for all the compiler knows err could be an object on the stack about to be unwinded, like e was at 0xbfbce430, not in the "magical location" at 0x98e7058), so you will lose derived-class-specific data during the copy-construction of a base class instance.

    Simple program to illustrate what's happening:

    #include <stdio.h>
    
    struct MyErr {
      MyErr() {
        printf("  Base default constructor, this=%p\n", this);
      }
      MyErr(const MyErr& other) {
        printf("  Base copy-constructor, this=%p from that=%p\n", this, &other);
      }
      virtual ~MyErr() {
        printf("  Base destructor, this=%p\n", this);
      }
    };
    
    struct MyErrDerived : public MyErr {
      MyErrDerived() {
        printf("  Derived default constructor, this=%p\n", this);
      }
      MyErrDerived(const MyErrDerived& other) {
        printf("  Derived copy-constructor, this=%p from that=%p\n", this, &other);
      }
      virtual ~MyErrDerived() {
        printf("  Derived destructor, this=%p\n", this);
      }
    };
    
    int main() {
      try {
        try {
          MyErrDerived e;
          throw e;
        } catch (MyErr& err) {
          printf("A Inner catch, &err=%p\n", &err);
          throw;
        }
      } catch (MyErr& err) {
        printf("A Outer catch, &err=%p\n", &err);
      }
      printf("---\n");
      try {
        try {
          MyErrDerived e;
          throw e;
        } catch (MyErr& err) {
          printf("B Inner catch, &err=%p\n", &err);
          throw err;
        }
      } catch (MyErr& err) {
        printf("B Outer catch, &err=%p\n", &err);
      }
      return 0;
    }
    

    Result:

      Base default constructor, this=0xbfbce430
      Derived default constructor, this=0xbfbce430
      Base default constructor, this=0x98e7058
      Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
      Derived destructor, this=0xbfbce430
      Base destructor, this=0xbfbce430
    A Inner catch, &err=0x98e7058
    A Outer catch, &err=0x98e7058
      Derived destructor, this=0x98e7058
      Base destructor, this=0x98e7058
    ---
      Base default constructor, this=0xbfbce430
      Derived default constructor, this=0xbfbce430
      Base default constructor, this=0x98e7058
      Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
      Derived destructor, this=0xbfbce430
      Base destructor, this=0xbfbce430
    B Inner catch, &err=0x98e7058
      Base copy-constructor, this=0x98e70b0 from that=0x98e7058
      Derived destructor, this=0x98e7058
      Base destructor, this=0x98e7058
    B Outer catch, &err=0x98e70b0
      Base destructor, this=0x98e70b0
    

    Also see: