Reputation: 942
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;
}
Upvotes: 2
Views: 5662
Reputation: 99535
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;
}
};
Upvotes: 2
Reputation: 283624
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.
Upvotes: -1
Reputation: 45239
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;
}
Upvotes: 0
Reputation: 146910
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;
}
}
Upvotes: 0
Reputation: 190897
Not really. my_instance
will be an invalid instance that can't be used.
http://www.gotw.ca/publications/mill13.htm
Upvotes: 3