简体   繁体   中英

Can exception handling of object whose constructor throw an exception be near its stack-based creation on code?

I'm trying to make my C++ code exception-safe, and got a problem that neither asking friends nor searching web will help.

On my understanding, when creating an object with constructor potentially throw an exception, the code for creation needs to be enclosed with try block and exception handling is done in catch(){} .

If the creation is heap-based(eg new ed with default allocator), I can place exception handling code near the creation like this:

void f() {
  // work unrelated to Hoge object here

  try {
    Hoge *pHoge = new Hoge(); // could throw an exception
  } catch(HogeException& ex) {
    // handle exception
  }

  // rest of work here
}

However, if the creation is stack-based, I can't find ways to do that and resort to code like below due to the scope of try block:

void g() {
  // work unrelated to Hoge object here

  try {
    Hoge hoge; // could throw an exception

    // rest of work here
  } catch(HogeException& ex) {
    // handle exception
  }
}

If // rest of work code above is large, the distance of locations between object creation and exception handling could be long, decreasing code readability...

I prefer the exception handling code is near object creation(and maybe that is one of the concepts of try - catch structure). Is there any solutions?

Delegate the // rest of work to a helper function, and pass a Hoge& to that function:

void RestOfWork(Hoge& hoge)
{
  // rest of work here
}

void g() {
  // work unrelated to Hoge object here

  try {
    Hoge hoge;
    RestOfWork(hoge);
    // rest of work here
  } catch(HogeException& ex) {
    // handle exception
  }
}

Incidentally, Hoge hoge(); doesn't do what you think it does. You probably think that you are declaring an object named hoge of type Hoge , and initializing it by calling the default constructor. What you're actually doing is declaring a function named hoge which takes no parameters and returns a Hoge by-value. I've fixed this in my code above.

Edit Indeed, as suggested by @LightnessRacesInOrbit, the construction of the Hoge object can take place in the deferral function as well, such as with:

  void RestOfWork()
  {
       Hoge hoge;
      // rest of work here
  }

  void g() {
    // work unrelated to Hoge object here

    try {
      RestOfWork();
    } catch(HogeException& ex) {
      // handle exception
    }
  }

A reasonable way to do this is to use a nullable single-item container, eg boost::optional :

void g() {
  // work unrelated to Hoge object here

  boost::optional<Hoge> opt_hoge;
  try {
    opt_hoge = boost::in_place<Hoge>();
  } catch(HogeException& ex) {
    // handle exception
  }
  Hoge &hoge = *opt_hoge;

  // rest of work here
}

If you can't use Boost, std::unique_ptr would work at the cost of a heap allocation:

void g() {
  // work unrelated to Hoge object here

  std::unique_ptr<Hoge> opt_hoge;
  try {
    opt_hoge = std::unique_ptr<Hoge>(new Hoge);
  } catch(HogeException& ex) {
    // handle exception
  }
  Hoge &hoge = *opt_hoge;

  // rest of work here
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM