6

For a school project, I have 3 classes: an Egg, a Nest, and a Hen. We need to use new to create an instance of each in main, call display() on each, then explicitly delete each. That's all easy.

My problem is not knowing how to properly catch a bad_alloc; should 1 be thrown on any of the new calls.

Right now, it looks like this:

int main(int argc, char* argv[]) {
    using namespace std;

    cout << "Creating new instances on the heap..." << endl;
    Egg* egg = new Egg("New Egg");
    Nest* nest = new Nest("New Nest");
    Hen* hen = new Hen("New Hen");

    cout << sizeof(*egg) << endl;
    cout << sizeof(*nest) << endl;
    cout << sizeof(*hen) << endl;


    cout << "\nCalling display() on each..." << endl;
    egg->display();
    nest->display();
    hen->display();

    cout << "\nExplicitly deleting each instance..." << endl;
    delete egg;
    delete nest;
    delete hen;


    cout << "\nDone" << endl;

}

I thought of wrapping the entire block from the first new to the last delete in a try block, then just catching a bad_alloc, and calling delete on each instance, but then I thought of the following scenario:

  • egg is successfully created
  • nest fails, and throws a bad_alloc

If I call delete on all 3 at this point, hen should throw another exception because it was never allocated in the first place, so it can't be free'd.

I know ideally, you wouldn't use new out in the open like this, but what's the best way to handle this situation? Is it too trivial and artificial to be properly handled?

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • _"My problem is not knowing how to properly catch a `bad_alloc`; should 1 be thrown on any of the `new` calls."_ You wrap one around all of them or have single `try {} catch {}` blocks for each of them? – πάντα ῥεῖ Mar 22 '15 at 16:13
  • Normally you'd use one of the smart pointer classes. `unique_ptr` might be a good fit here. Your assignment doesn't seem to allow it, though. –  Mar 22 '15 at 16:13
  • 3
    Actually, it's very well possible that you're not expected to know about the possibility of `new` failing yet, and that you're not expected to handle it. –  Mar 22 '15 at 16:15
  • 1
    **+1** This is a ***very*** good question. It is scenarios like this that smart pointers and other scoped "decommissioners" were designed for. I believe Ed Heal's answer will work in your case. – Galik Mar 22 '15 at 16:18
  • @hvd You're right. At this point we aren't required to deal with such situations. My problem is that 20% of my mark for the program is a "test plan" that requires testing at least 3 scenarios where the program can fail/do something unintended. The only i/o it does though is the `cout`s in main, and the `display()` method. It's basically just 3 nested classes, 2 of which contain an instance of one of the other 3 classes. I'm grasping at straws to find a failure point; let alone 3. Thus, this question. – Carcigenicate Mar 22 '15 at 16:56
  • 1
    @hvd: I think what's more important is that normally you'd not use dynamic allocation for these objects in the first place. This is IMO the #1 guideline for C++ dynamic memory allocation: "When in doubt, don't." Only then should `std::unique_ptr` et al enter the game. – Christian Hackl Mar 22 '15 at 17:29
  • @Carcigenicate: Basically, thinking about test plans is a very good. But I wonder myself how three different failure scenarios should be created if there is no input, your only communication with external resources is memory allocation and we suppose your code is error-free. – Christian Hackl Mar 22 '15 at 17:38
  • @ChristianHackl The test plan requirement is across every project that we do. Most contain something like reading from a file or stdIn, or converting a String to an int, so usually there's something that can fail. This one is overly simple though. – Carcigenicate Mar 22 '15 at 18:30
  • @ChristianHackl and the requirement for the project was to dynamically allocate the 3 in main; regardless of how little sense it makes in this case. – Carcigenicate Mar 22 '15 at 18:33

2 Answers2

6

You can do this

  1. Declare them first. Set to nullptr
  2. Start the try block
  3. Allocate.
  4. Catch - delete them - delete nullprt is a noop
Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • 1
    Awesome, thank you. What does "is a noop" mean? It will just silently allow it? – Carcigenicate Mar 22 '15 at 18:41
  • 1
    @Carcigenicate Yes, "noop" stands for "no operation". The fact that `delete nullptr` is a noop means that deleting a null pointer is guaranteed to do nothing. This implies that it's not an error to do so. – chi Mar 22 '15 at 19:13
0

I guess you could write a long program like the following (but I do not know if it is necessarily better)

int main(int argc, char* argv[]) {
  using namespace std;
  Egg* egg;
  Nest* nest;
  Hen* hen;

  cout << "Creating new instances on the heap..." << endl;
  try {
    egg = new Egg("New Egg");
  } catch (std::bad_alloc& ba) {
     std::cerr << "bad_alloc caught: " << ba.what() << '\n';
     return 1;
  }

 try {
    nest = new Nest("New Nest");
  } catch (std::bad_alloc& ba) {
     std::cerr << "bad_alloc caught: " << ba.what() << '\n';
     delete egg;
     return 1;
  } 

  try {
    hen = new Hen("New Hen");
  } catch (std::bad_alloc& ba) {
     std::cerr << "bad_alloc caught: " << ba.what() << '\n';
     delete egg;
     delete nest;
     return 1;
  }

  cout << sizeof(*egg) << endl;
  cout << sizeof(*nest) << endl;
  cout << sizeof(*hen) << endl;


  cout << "\nCalling display() on each..." << endl;
  egg->display();
  nest->display();
  hen->display();

  cout << "\nExplicitly deleting each instance..." << endl;
  delete egg;
  delete nest;
  delete hen;


  cout << "\nDone" << endl;
  return 0;
}
JoshOvi
  • 88
  • 7
  • Woah Nelly. I considered something along these lines, but I didn't like how long it would make the main procedure. – Carcigenicate Mar 22 '15 at 18:43
  • @Carcigenicate And this would be one reason why any reasonably modern C++ developer would not do it this way. That's what `std::make_unique` is for (and other smart pointers). Let the RAII techniques do their job. (Heck, in this simplistic example, there's no point in dynamically creating the objects in the first place...) – Andre Kostur Mar 22 '15 at 22:10
  • @AndreKostur Thanks. This is like the fourth mention of smart pointers, so I'll definitely look them up; although depending on what they look like, might be overkill for this particular example. – Carcigenicate Mar 22 '15 at 22:30