2

I sometimes need a class in C++ which allocates dynamic memory. Since this can fail, I need to detect when the memory cannot be allocated. Usually, I do this as in the example below, ie. I do not allocate memory in the constructor but have a separate method for this, where a bad_alloc exception can be caught.

Is there any way to allocate memory in the constructor and catch an exception?

try {
  my_class my_instance;
}
catch ...

does not work because the scope of my_instance is limited to the try block.

Here is a minimal example:

#include <iostream>

class my_class {
private:
  char * data;

public:
  my_class () {
    data = NULL;
  }

  ~my_class () {
    delete [] data;
  }

  void init () {
    data = new char [10000000000];
  }

  void write (int x) {
    data[x] = 1;
  }
};

int main() {
  my_class my_instance;
  try {
    my_instance.init();
  }
  catch (std::bad_alloc&) {
    std::cout << "Memory overflow.\n";
    return 1;
  }

  my_instance.write(10);

  std::cout << "OK.\n";
  return 0;
}
Daniel
  • 924
  • 1
  • 11
  • 16
  • Similar Question? http://stackoverflow.com/questions/4989807/how-to-handle-failure-in-constructor-in-c/4989828#4989828 – Lior Kogan Feb 21 '11 at 19:06
  • Not sure about it, but how about using `my_class *my_instance_ptr` and assign this in the `try..catch` block? – schnaader Feb 21 '11 at 19:09

5 Answers5

3

Not really. my_instance will be an invalid instance that can't be used.

http://www.gotw.ca/publications/mill13.htm

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
2

You could use little-known feature introduced in the C++ Standard in 1995 — function-try-block as follows:

struct A
{
private:
  char* data;
public:
  // catch exception in the constructor
  A() try : data( new char [10000000000] ) {}
  catch ( std::bad_alloc ) { data = NULL; /* handle bad_alloc exception here */ }

  void write (int x) {
    if ( data ) data[x] = 1;
  }
};

This approach is not usable if A is inherited from some base class. And the fact that you can catch bad_alloc exception gives you nothing in the end. If you can work with the less amount of the allocated memory you could use std::get_temporary_buffer instead of new:

struct A
{
private:
  std::pair<char*,ptrdiff_t> data;
public:
  // get as much memory as possible
  A() : data( std::get_temporary_buffer<char>(10000000000) ) {}
  ~A() { std::return_temporary_buffer( data.first ); }

  void write (int x) {
    if ( x < data.second ) // check x is in the allocated range
      data.first[x] = 1;
  }
};
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • The *function-try-block* will rethrow the exception. – Ben Voigt Feb 21 '11 at 19:29
  • Although you raise a point, your example brings a solution far worse than the problem it's trying to solve. You're hiding the fact that the exception occurred, almost guaranteeing an access violation in the `write()` method. – André Caron Feb 21 '11 at 19:30
  • Isn't there a restriction on `get_temporary_buffer()` to release the acquired memory before returning from the function? Not sure what the standard says, but the SGI documentation says "As the name suggests, get_temporary_buffer should only be used to obtain temporary memory. If a function allocates memory using get_temporary_buffer, then it must deallocate that memory, using return_temporary_buffer [3], before it returns." – André Caron Feb 21 '11 at 19:39
  • @André Caron, C++ Standard doesn't have such restriction. Take a look on [this question](http://stackoverflow.com/questions/3264299/why-do-i-need-stdget-temporary-buffer). – Kirill V. Lyadvinsky Feb 21 '11 at 19:42
0

Yeah- it's called writing it in the try.

int main() {
    try {
        my_class my_instance;
        my_instance.write(10);
        std::cout << "OK.\n";
        return 0;
    }
    catch (std::bad_alloc&) {
        std::cout << "Memory overflow.\n";
        return 1;
    }
}
Puppy
  • 144,682
  • 38
  • 256
  • 465
0

I'm not sure what's wrong with the constructor throwing an exception, and I certainly wouldn't use an init() method. Refer to The C++ Programming Language, there's a note somewhere on 4 key reasons not to use one.

I usually write my main() function like this to avoid a core dump and write to the application's debug log to make sure there is some note of what, exactly, happened.

int main ( int argc, char ** argv )
try
{
    // do some stuff
    //...
    return EXIT_SUCCESS;
}
    // lots of stuff, including `std::bad_alloc`.
catch ( const std::exception& )
{
    // write to application debug log.
    // ...
    return EXIT_FAILURE;
}
    // any unkonwn exception, possibly from 3rd-party library.
catch ( ... ) 
{
    // write to application debug log.
    // ...
    return EXIT_FAILURE;
}
André Caron
  • 44,541
  • 12
  • 67
  • 125
-1

How about using the non-throwing allocator...

my_class(void)
   : data(new (std::nothrow) char[1000000])
{
}

bool isvalid(void) const { return data != 0; }

Although a function-try-block is a good way to log the failure, it can't rescue the object (from draft 3225, section [except.handle]):

The fully constructed base classes and members of an object shall be destroyed before entering the handler of a function-try-block of a constructor for that object.

The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720