Link to home
Start Free TrialLog in
Avatar of chetmunk700
chetmunk700

asked on

exception handler for bad alloc exception.

Im a newbie at c++ was confused on how to put a exception handler in here to catch a bad alloc exception if the user attempts to allocate an array object and insufficient memory is available.

here is what i have
class array 
{ 
 public: 
  
array( int = 1 ); 
  
// other functions not needed for this assignment 
 private: 
  
int*  elts; 
  
int  count; 
}; 
 
array::array( int sz ) 
{ 
 count = sz; 
 elts  = new int[ count ]; 
} 
 
int main( ) 
{ 
 array* ptr[10]; 
 
 for ( int i = 0; i < 10; i++ ) 
 { 
  
ptr[i] = new array( 100000000L ); 
 } 
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of theKashyap
theKashyap

Do note that there 3 overloads of new available usually.

void* operator new (std::size_t size) throw (std::bad_alloc);
First one, which is used by default by everyone throws bad_alloc on error.

There are 2 others.
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw();
This one returns NULL instead of throwing bad_alloc in case it fails to allocate memory.

void* operator new (std::size_t size, void* ptr) throw();
This is the third/last one. It's useful when you're dealing with large number of objects. It improves the performance many fold when used in correct usecases (your case is one such case). An example here.

Given below your code modified to use placement new. It's ~25 times faster for 100 array object of 5000000 ints each.

Examples of all 3 new:
using namespace std;

class array {
public:
    array( const int = 1);
    array( const int, void*& buffer );

// other functions not needed for this assignment
private:
    int*  elts;
    int  count;
};

array::array( const int sz ) {
    try {
        elts  = new int[ sz ];
        count = sz;
    } catch (bad_alloc) {
        cout << "array::array() -- Failed to allocate: " << sz << endl;
    }
}

array::array( const int sz, void*& buffer ) {
    // cout << "array::array() -- entry sz = " << sz << endl << "buffer = " << buffer << endl;
    // As placement new does NOT allocate memory it never fails unless the c'tor
    // of the type throws an error.
    elts  = new (buffer) int[ sz ];

    // forward the ptr beyond the used memory, so it points to
    // the remainder of free memory.
    (int*&)buffer += (sz * sizeof(int));

    count = sz;
}

int main( ) {
    const int ARR_SIZE = 5000000;
    const int LOOP_SIZE = 100;
    array* ptr[LOOP_SIZE];

    void* my_buffer = malloc(ARR_SIZE * LOOP_SIZE * sizeof(int));
    if ( NULL == my_buffer ) {
        cout << "Failed to allocate buffer." << endl;
        return 1;
    }

    for ( int i = 0; i < LOOP_SIZE; i++ ) {
        ptr[i] = new array( ARR_SIZE, my_buffer );
    }

    // either use free() to do a bulk free or use the normal delete
    // operator if you want to deallocated element by element.

    return 0;
}

Open in new window


A good reference: http://www.cplusplus.com/reference/std/new/operator%20new/
Just to note that you should always catch exceptions by reference (const normally) and not value!

catch (bad_alloc  const &)

Open in new window


When you catch by value you create a new copy of the exception. Also if the exception is caught by value and you happen to catch a superclass you will slice the object if the original exception was a subclass.
Oh, and one other thing - to save you messing around with new and delete (and possibly leaking memory) why don't you just use a vector?

http://www.cplusplus.com/reference/stl/vector/
@evilrix: Think it's an assignment to learn new operator.
>> you should always catch exceptions by reference (const normally) and not value!
>> catch (bad_alloc  const &)
My reference book says:
     catch (bad_alloc)

It looks like for bad_alloc, this is Ok.

>> Also if the exception is caught by value and you happen to catch a superclass you will slice the object if the original exception was a subclass.
This remark can be generalized as a legitimate concern in discussing passing function arguments, but we still can pass by value for specific situations.
>> It looks like for bad_alloc, this is Ok.
It's still creating an unnecessary copy :)

>> but we still can pass by value for specific situations.
Sure you can but in the case of exceptions the standard ones are polymorphic and if you catch by value and not by exception you will slice. For example, there is nothing to stop a library sub-classing bad_alloc and throwing a sub-class version of it. Boost often subclasses standard exceptions.

In the end, it is just something for the asker to be aware of.
>> It's still creating an unnecessary copy :)
   catch (bad_alloc  const &)
   catch (bad_alloc)
Something is being copied in both cases. Not much of a difference in quantity copied.
just another point to be noted since I saw some code using malloc in this thread.

Do not mix malloc and new in your program and if you must do it ensure that memory allocated using malloc is freed using free() and not delete. Likewise for new, only use delete to release memory allocated using new
>Something is being copied in both cases.
In the case of catching by reference the reference is bound to the thrown object, in the case of the second the copy constructor will be called.
theKashyap,

Please note that your article is not yet published.