What's the best practice to prevent memory leak if an exception thrown in constructor?

前端 未结 4 510
清酒与你
清酒与你 2021-02-08 02:55

I know if an exception is thrown in constructor, destructor will not be called(simple class, no inheritance). So if an exception is thrown in constructor and there is a chance s

4条回答
  •  不思量自难忘°
    2021-02-08 03:34

    I would stick to the RAII idiom.

    If you avoid "naked" resources (like operator new, naked pointers, naked mutexes, etc.) and instead wrap everything into a container or class with proper RAII behavior you will not have the problems you describe, even in the presence of exceptions.

    That is, do not acquire naked resources in your constructor. Instead, create an instance of an object which itself follows RAII. That way, even if your constructor fails (that is, the one which creates the instances), the destructors of the objects which were initialized will be called.

    So, this is bad practice:

    #include
    #include
    
    struct Bad {
      Bad() {
        double *x = new double;
        throw(std::runtime_error("the exception was thrown"));
      }
    
      ~Bad() {
        delete x;
        std::cout<<"My destructor was called"<

    Output:

    We have a leak! Let's keep going!
    Here I am... with a leak...
    

    Compare with this contrived and silly good implementation:

    #include
    #include
    
    struct Resource {
    
      Resource() {
        std::cout<<"Resource acquired"<

    Output:

    Acquiring resource
    Resource acquired
    Resource cleaned up
    We DO NOT have a leak! Let's keep going!
    Here I am... without a leak...
    

    My point is the following: try to encapsulate all resources which need to be liberated into their own class where the constructor does NOT throw, and the destructor correctly releases the resource. Then, on the other classes where the destructor may throw, simply create instances of the wrapped resource and the destructors of the acquired resource wrappers will be guaranteed to clean-up.

    The following is probably a better example:

    #include
    #include
    #include
    
    // a program-wide mutex
    std::mutex TheMutex;
    
    struct Bad {
      Bad() {
        std::cout<<"Attempting to get the mutex"<lock();
        std::cout<<"Got it! I'll give it to you in a second..."<unlock();
        std::cout<<"There you go! I released the mutex!"<

    Output (compiled with gcc 4.8.1 using -std=c++11):

    Create a Good instance
    Attempting to get the mutex
    Got it! I'll give it to you in a second...
    There you go! I released the mutex!
    Ooops, I threw!
    Now, let's create a Bad instance
    Attempting to get the mutex
    Got it! I'll give it to you in a second...
    Ooops, I threw!
    Now, let's create a whatever instance
    Attempting to get the mutex
    

    Now, please don't follow my example and create your own scope guard. C++ (specially C++11) are designed with RAII in mind and provide a wealth of lifetime managers. For example, an std::fstream will automatically close, an [std::lock_guard][2] will do what I attempted to do in my example, and either std::unique_ptr or std::shared_ptr will take care of destruction.

    The best advice? Read about RAII (and design according to it), use the Standard Library, don't create naked resources, and get familiarized with what Herb Sutter has to say with respect to "exception safety" (go ahead and read his website, or google "Herb Sutter Exception Safety")

提交回复
热议问题