12

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 some heap memory is not cleaned up. So what's best practice here? let's assume I have to call some function in constructor and it may throw exception. Shall I always use shared pointer in this case? What's the alternatives? Thank you!

ycshao
  • 1,768
  • 4
  • 18
  • 32
  • @OtávioDécio that's rubbish. constructors are *the* place for throwing exceptions -- where else to complain if the initialisation parameters are non-sensical? – Walter Aug 19 '13 at 22:36
  • @ycshao Do you mean the memory you allocated or the memory that the function you call allocates, and throws, its memory? – hetepeperfan Aug 19 '13 at 22:44
  • 1
    @Walter I disagree. You can gain a lot of benefits from not throwing exceptions in the constructor (for example, `std::vector` will `push_back` using `std::move` if the constructor of `T` is declared `noexcept(true)`, otherwise it will copy). In addition, throwing exceptions unrolls the stack, which may be too expensive. Some prefer to use "two-phase constructors", where the constructor is `noexcept(true)` and the individual resources are acquired by methods which may or may not throw (or otherwise signal failure) in order to gain the benefits, – Escualo Aug 19 '13 at 23:45
  • @Arrieta Ok, I agree that copy and, in particular, move constructors should avoid throwing. However, they construct from an existing object, when checking sensibility of parameters is not an issue. Critical are constructors that take parameters which may have non-sensical values. – Walter Aug 20 '13 at 11:12
  • @Walter Good design can help avoid the "non-sensical" values (via type checking or similar constructs). Of course, you can always have, for example, a filename which does not exist, or similar type correct but still nonsensical constructor argument. On that case, again, some prefer to return error codes or similar "success flags" to avoid throwing exceptions and unwinding the stack. For example, dynamic_cast can return a null pointer as opposed to throwing bad_cast. I am "all for exceptions", but I really think that constructors are not necessarily the best place to throw them (they can be – Escualo Aug 20 '13 at 16:13
  • @hetepeperfan Some memory I allocated before the function throws in constructor. – ycshao Aug 20 '13 at 16:18

4 Answers4

18

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<iostream>
#include<stdexcept>

struct Bad {
  Bad() {
    double *x = new double;
    throw(std::runtime_error("the exception was thrown"));
  }

  ~Bad() {
    delete x;
    std::cout<<"My destructor was called"<<std::endl;
  }

  double *x;  
};

int main() {
  try {
    Bad bad;
  } catch (const std::exception &e) {
    std::cout<<"We have a leak! Let's keep going!"<<std::endl;
  }
  std::cout<<"Here I am... with a leak..."<<std::endl;
  return 0;
}

Output:

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

Compare with this contrived and silly good implementation:

#include<iostream>
#include<stdexcept>

struct Resource {

  Resource() {
    std::cout<<"Resource acquired"<<std::endl;    
  }

  ~Resource() {
    std::cout<<"Resource cleaned up"<<std::endl;        
  }

};

struct Good {
  Good() {
    std::cout<<"Acquiring resource"<<std::endl;
    Resource r;
    throw(std::runtime_error("the exception was thrown"));
  }

  ~Good() {
    std::cout<<"My destructor was called"<<std::endl;
  }  
};


int main() {
  try {
    Good good;
  } catch (const std::exception &e) {
    std::cout<<"We DO NOT have a leak! Let's keep going!"<<std::endl;
  }
  std::cout<<"Here I am... without a leak..."<<std::endl;
  return 0;
}

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<mutex>
#include<iostream>
#include<stdexcept>

// a program-wide mutex
std::mutex TheMutex;

struct Bad {
  Bad() {
    std::cout<<"Attempting to get the mutex"<<std::endl;
    TheMutex.lock();
    std::cout<<"Got it! I'll give it to you in a second..."<<std::endl;
    throw(std::runtime_error("Ooops, I threw!"));
    // will never get here...
    TheMutex.unlock();
    std::cout<<"There you go! I released the mutex!"<<std::endl;    
  }  
};

struct ScopedLock {
  ScopedLock(std::mutex& mutex)
      :m_mutex(&mutex) {
    std::cout<<"Attempting to get the mutex"<<std::endl;
    m_mutex->lock();
    std::cout<<"Got it! I'll give it to you in a second..."<<std::endl;    
  }

  ~ScopedLock() {
    m_mutex->unlock();
    std::cout<<"There you go! I released the mutex!"<<std::endl;        
  }
  std::mutex* m_mutex;      
};

struct Good {
  Good() {
    ScopedLock autorelease(TheMutex);
    throw(std::runtime_error("Ooops, I threw!"));
    // will never get here
  }  
};


int main() {
  std::cout<<"Create a Good instance"<<std::endl;
  try {
    Good g;
  } catch (const std::exception& e) {
    std::cout<<e.what()<<std::endl;
  }

  std::cout<<"Now, let's create a Bad instance"<<std::endl;
  try {
    Bad b;
  } catch (const std::exception& e) {
    std::cout<<e.what()<<std::endl;
  }

  std::cout<<"Now, let's create a whatever instance"<<std::endl;
  try {
    Good g;
  } catch (const std::exception& e) {
    std::cout<<e.what()<<std::endl;
  }

  std::cout<<"I am here despite the deadlock..."<<std::endl;  
  return 0;
}

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")

Escualo
  • 40,844
  • 23
  • 87
  • 135
  • 4
    RAII is the right approach, but resource-as-a-local-variable is a rare case. Usually you would be worried about resources which will end up owned by the object under construction. – Ben Voigt Aug 19 '13 at 23:19
  • @BenVoigt Agreed. Would what would you recommend in that case? Two-phase construction? – Escualo Aug 19 '13 at 23:40
  • 1
    You can initialize the smart pointers in the initializer list, using helper functions that perform allocation and return a smart pointer. Or you can have a local smart pointer and swap/move it to the member variable. But you came up with a good example of resource-as-a-local, with the mutex ownership scope. – Ben Voigt Aug 19 '13 at 23:56
  • Just a notice on RAII: this technique takes into consideration that in C++, the only code that can be guaranteed to be executed after an exception is thrown are the destructors of objects residing on the stack. By wrapping around the code and avoiding using "naked" resources, everything is based on the stack and the fact that those resources are going to be released for sure if an exception occurs. – Nick Louloudakis Aug 20 '13 at 00:02
  • @Ben Voigt: I said "By wrapping around the code and avoiding using "naked" resources...". as also said in the answer, meaning "avoiding the manual dynamic memory allocation". – Nick Louloudakis Aug 20 '13 at 00:28
  • @Ben Voigt: To be honest, this statement is mentioned on wiki: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization I am not 100% sure about it, but I think that the only destructors called are of objects on the stack. The dynamic objects have their memory allocated on the heap, and are destructed via the delete calls in code (which implicitly calls their destructors) and not automatically. See here too: http://stackoverflow.com/questions/677653/does-delete-call-the-destructor-in-c I like this conversation, as I think we get things from it, at least I do. – Nick Louloudakis Aug 20 '13 at 03:52
  • @BenVoigt: the idiom handles resource-as-owned-member just as well. – n. m. could be an AI Aug 20 '13 at 08:10
  • @n.m.: With some caveats... it's not good to use `operator new` directly in the *ctor-initializer-list*, for exception safety you need a helper such as `make_unique`. – Ben Voigt Aug 20 '13 at 14:10
  • @Ben Voigt: You are right. I guess that the point on wiki was that it is not guaranteed that manually allocated objects are not guaranteed to be freed at all - in fact, there is a big potential of memory leaks and that is what RAII is trying to prevent. Should we delete our comments? – Nick Louloudakis Aug 20 '13 at 16:04
  • I still doesn't understand this part "try to encapsulate all resources which need to be liberated into their own class where the constructor does NOT throw". How can we be sure the constructor would not throw if we would be using new in that class's constructor? To my eyes it looks like we can't guarantee No resource leak with 100% certainty. Is that correct? – Infinity Jan 22 '22 at 05:41
3

Avoid the need to allocate memory on the heap (via new and new[]) by using standard library containers. If this is not possible, always use a smart pointer, like std::unique_ptr<> to manage memory allocated on the heap. Then you will never need to write code for deleting the memory and it will be automatically cleaned up even in case an exception is thrown in your constructor (actually a constructor is often a likely place for an exception, but a destructor should really not throw).

Walter
  • 44,150
  • 20
  • 113
  • 196
  • 3
    Another way of saying this is to implement your classes in such a way that the default destructor is sufficient. – jxh Aug 19 '13 at 22:50
1

If you must handle a resource, and your use case is not handled by any of the utilities in the standard library, then the rule is simple. Handle one, and only one resource. Any class that needs two resources handled should store two objects that are capable of handling themselves (i.e. objects that follow RAII). As a simple example of what not to do, lets say you wanted to write a class that needed a dynamic array of ints, and a dynamic array of doubles, (forget the standard library for the moment). What you would not do is this:

class Dingbat
{
public:
    Dingbat(int s1, int s2)
    {
        size1 = s1;
        size2 = s2;
        a1 = new int[s1];
        a2 = new int[s2];
    }
    ...
private:
    int * a1;
    double * a2;
    int size1, size2;
};

The problem with the above constructor is that if the allocation for a2 fails, an exception is thrown, and the memory for a1 is not released. You could of course handle this with try catch blocks, but it becomes much more complex (needlessly) when you have multiple resources.

Instead, you should write classes (or a single class template in this case) that handles a single dynamic array properly, taking care of initializing itself, copying itself and disposing of itself. If there is only one call to new, then you don't need to worry if the allocation fails. An exception will be thrown and no memory needs to be released. (you may want to handle it anyway and throw your own custom exception in order to be more informative)

Once you have that/those classes all finished, then your Dingbat class would include each of these objects. The Dingbat class is then much simpler, and probably doesn't need any special routines for handling initialization, copying or destruction.

This example is, of course, hypothetical, as the above situation is handled already by std::vector. But like I said, this is for if you happen to have a situation that isn't covered by the standard library.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
1

What you can very often do is call the function that can fail before the constructor, and call the instructor with the value that the function that could fail returns.

#include <string>
#include <iostream>
#include <memory>

class Object {};

This is just some Object that our class needs. It could be a connected socket, or a bound socket. Something that can fail when it is trying to connect or bind inside a constructor.

Object only_odd( int value ) {
    if ( value % 2 == 0 )
        throw "Please use a std::exception derived exception here";
    else
        return Object();
}

This function returns a object and throws when it fails (for every even number). So this could be what we first would have like to done in the destructor.

class ugly {
    public:
        ugly ( int i ) {
            obj = new Object;
            try{
                *obj = only_odd( i );
            }
            catch ( ...) {
                delete obj;
                throw ( "this is why this is ugly" );
            }
        }

        ~ugly(){ delete obj; }

    private:

        Object* obj;
};

better takes the pre constructed value that might fail and therefore throw. Therefore we could also construct the better class from an already initialised object. Then we can do error handling even before the class gets constructed and then we do not have to throw from the constructor. And even better, it uses smart pointers for memory handling, that way we can be really pretty sure the memory gets deleted.

class better {

    public:

        better ( const Object& org ) : obj { std::make_shared<Object>(org) }
        {
        }

    private:
        /*Shared pointer will take care of destruction.*/
        std::shared_ptr<Object>  obj;
};

and this might be how we would use it.

int main ( ) {
    ugly (1);

    /*if only odd where to fail it would fail allready here*/
    Object obj = only_odd(3); 
    better b(obj);

    try { /*will fail since 4 is even.*/
        ugly ( 4  );
    }
    catch ( const char* error ) {
        std::cout << error << std::endl;
    }
}
hetepeperfan
  • 4,292
  • 1
  • 29
  • 47