Is it ever not safe to throw an exception in a constructor?

前端 未结 5 1768
长情又很酷
长情又很酷 2020-12-05 03:46

I know that it\'s not safe to throw exceptions from destructors, but is it ever unsafe to throw exceptions from constructors?

e.g. what happens for objects that ar

5条回答
  •  既然无缘
    2020-12-05 04:00

    Throwing exceptions from a constructor is a good thing. When something fails in a constructor, you have two options:

    • Maintain a "zombie" state, where the class exists but does nothing, or
    • Throw an exception.

    And maintaining zombie classes can be quite a hassle, when the real answer should have been, "this failed, now what?".

    According to the Standard at 3.6.2.4:

    If construction or destruction of a non-local static object ends in throwing an uncaught exception, the result is to call terminate (18.6.3.3).

    Where terminate refers to std::terminate.


    Concerning your example, no. This is because you aren't using RAII concepts. When an exception is thrown, the stack will be unwound, which means all objects get their destructor's called as the code gets to the closest corresponding catch clause.

    A pointer doesn't have a destructor. Let's make a simple test case:

    #include 
    
    int main(void)
    {
        try
        {
            std::string str = "Blah.";
            int *pi = new int;
    
            throw;
    
            delete pi; // cannot be reached
        }
        catch(...)
        {
        }
    }
    

    Here, str will allocate memory, and copy "Blah." into it, and pi will be initialized to point to an integer in memory.

    When an exception is thrown, stack-unwinding begins. It will first "call" the pointer's destructor (do nothing), then str's destructor, which will free the memory that was allocated to it.

    If you use RAII concepts, you'd use a smart pointer:

    #include 
    #include 
    
    int main(void)
    {
        try
        {
            std::string s = "Blah.";
            std::auto_ptr pi(new int);
    
            throw;
    
            // no need to manually delete.
        }
        catch(...)
        {
        }
    }
    

    Here, pi's destructor will call delete and no memory will be leaked. This is why you should always wrap your pointers, and is the same reason we use std::vector rather than manually allocating, resizing, and freeing pointers. (Cleanliness and Safety)

    Edit

    I forgot to mention. You asked this:

    I think I want to put an autoptr around P and call release on the autoptr after dostuff to prevent a memory leak, would that be correct?

    I didn't state it explicitly, and only implied it above, but the answer is no. All you have to do is place it inside of auto_ptr and when the time comes, it will be deleted automatically. Releasing it manually defeats the purpose of placing it in a container in the first place.

    I would also suggest you look at more advanced smart pointers, such as those in boost. An extraordinarily popular one is shared_ptr, which is reference counted, making it suitable for storage in containers and being copied around. (Unlike auto_ptr. Do not use auto_ptr in containers!)

提交回复
热议问题