(not) using std::string in exceptions

前端 未结 2 865
猫巷女王i
猫巷女王i 2020-12-28 14:44

I\'m always reading that I should not to throw a std::string or some other classes allocating memory. like here or more importantly here on point 3. - D

相关标签:
2条回答
  • 2020-12-28 15:18

    The advice is basically telling you "Don't use any construct that might throw an exception in an exception". That's because if you get an exception while trying to throw an exception, the C++ runtime will just immediately call terminate() and kill your program.

    Now if (either) of the exceptions involved would just call terminate() anyways (as is the default for an uncaught exception), then you don't really need to worry about it. For example, if your application can't handle bad_alloc (can't recover from out-of-memory), then you don't need to worry about copy constructors (such as std::string) that might throw it.

    But if you want to be able to catch and recover from a bad_alloc, you need to ensure that none of your exception copy constructors can cause one. If you're writing a library that other applications will use, you should not assume that the application does not want to handle bad_alloc.

    C++11 make this much easier by using move constructors (instead of copy constructors) where possible. Since the move constructor for std::string never throws exceptions you can safely use a std:string in your exception type as long as you properly implement move constructors, and ensure that they are used. Note that the initial construction of the object to be thrown in a throw expression is NOT part of the exception throwing process, so that constructor can throw an exception without causing a double exception (and terminate()). So if you have:

    throw some_function();
    

    some_function might throw an exception (such as bad_alloc) without returning an object to be thrown and that's fine. If it doesn't throw an exception (and returns a valid object), the move constructor for the exception type will be used (if available) for the exception throwing process, and that move constructor must not throw an exception.


    Completely independent of the above, whenever you call new you need to ensure that exactly one spot will call delete in every possible case, or you'll leak memory (or crash from a double delete). This becomes tricky any time you have a function that calls new and then does something else that might throw an exception (such as call new again). If this happens in a constructor, the destructor for the object will not be called (though destructors for base classes and fields will be), so you can't do the cleanup in the destructor as you are trying to do with your example.

    Fortunately std::unique_ptr exists to make this much easier. If you write your exception class as:

    struct xexception {
      std::unique_ptr<int[]> ttt[10];
      xexception() {
        ttt[0].reset(new int[0xfffffffL]);
        ttt[1].reset(new int[0xfffffffL]);
        ttt[2].reset(new int[0xfffffffL]);
        ttt[3].reset(new int[0xfffffffL]);
        ttt[4].reset(new int[0xfffffffL]);
        ttt[5].reset(new int[0xfffffffL]);
        ttt[6].reset(new int[0xfffffffL]);
        ttt[7].reset(new int[0xfffffffL]);
        ttt[8].reset(new int[0xfffffffL]);
        ttt[9].reset(new int[0xfffffffL]);
      }
    };
    

    it should work and not leak memory.

    0 讨论(0)
  • 2020-12-28 15:28

    While I think not using std::string for core, fundamental exceptions may be a good guideline, I don't think user-facing libraries/applications should necessarily follow this.

    There may be other reasons, but you hit on the primary one: you'd like to indicate to the user (or developer) contextually meaningful information, which you often cannot do with a mere literal string. A dynamic allocation must occur in order to do this. If, for some reason, you had a bad_alloc, you're probably already hosed to begin with, so it doesn't buy/lose you anything.

    Edit:

    By the way: the destructor of std::exception is marked as virtual for a reason!

    0 讨论(0)
提交回复
热议问题