AturSams
AturSams

Reputation: 7952

Handle an exception thrown from a CTOR?

tl;dr

I want to do something like this:

foo()
{
  Y* p;
  try {
    p = new Y();
  } catch {
    //fix any problem that may have occured
  }
  // Now I know the object was fixed during catch or simply was created successfully.
  p.do_something();
  // win
}

The object I am creating is using an eternal resource that is sometimes unreliable. It is dangerous to attempt to allocate without necessary precautions. If it does fail, there are things that could be done. However it seems that throwing an exception unlike the switch statement I've show later, does not allow me to fix the problem AFAIK.

I have been advised to throw an exception from a constructors. However, I don't understand how that can prove to be useful because I don't see how that exception will be handled? I did a search and found this example but I am not sure if it's really a useful extendable way to handle construction failure.

example code:

void f()
{
  X x;             //← if X::X() throws, the memory for x itself will not leak
  Y* p = new Y();  //← if Y::Y() throws, the memory for *p itself will not leak
}

Lets say there were more instances on the heap that were allocated before p. Wouldn't they leak to memory because of this? So the function f is just used to construct an instance of Y. Would it not be always more useful to move the "dangerous" construction outside to another method?

What I normally do:

X* x = new X(); //No dangerous operation

switch (x.init()) // init returns int
  {
    case ...
    // Handle failed init() here
  }

Are there disadvantages to this? It seems more reliable.

Upvotes: 0

Views: 313

Answers (3)

Michael Hagar
Michael Hagar

Reputation: 646

You should use RAII (Resource acquisition is initialization) on each of the members. A destructor for an object is only called if the constructor has completed. That way, if only one of the two members' constructors is called, only that member's destructor will be called, in which any cleanup should be performed.

class Locked_file_handle {
    File_ptr p;
    unique_lock<mutex> lck;
public:
    X(const char* file, mutex& m)
        :p{file, "rw"},
        lck{m} 
    {}
    // ..
};

If an exception occurs after p has been constructed but before lck has been, then the destructor for p but not for lck will be invoked. Doing it this way will let the author of the constructor forgo writing explicit exception handling code.

Source : "The C++ Programming Language 4th edition" (Stroustrup)

Edit: So in your case, it would look like this

public class YHandle {
    Y* p;
    YHandle() {
        Y = new Y()
    }
    ~YHandle() {
        delete Y;
    }
}

foo() {
    YHandle p = YHandle();
    p.do_something();
} // p is deleted here

Upvotes: 2

Simon Richter
Simon Richter

Reputation: 29598

Your example is safe, because if the c'tor throws an exception, the memory allocated for the object that failed to construct is freed before the exception is passed on. When the exception exits the f scope, the stack is unwound.

If a half-constructed-and-subsequently-having-its-members-and-bases-destructed object were returned to the calling scope, there would be no way for that scope to determine how to properly free that memory, so thankfully this isn't done.

If other objects are constructed on the heap in the same function, then the usual rules apply. In principle, you do not need to special-case c'tor calls here, and just pretend that the statement that throws is a regular function call.

Upvotes: 1

SHR
SHR

Reputation: 8313

If the throw may cause a leak then it is your responsible te delete the allocation

you can roll the exception like this:

void f()
{
  X x;             //← if X::X() throws, the memory for x itself will not leak
  Y* p;
  try{  
    p = new Y();  //← if Y::Y() throws, the memory for *p itself will not leak
  } catch(exception &e){
    ///clean what you need

    throw e;
  }
}

Upvotes: -1

Related Questions