A good way of handling errors from a constructor?

Started by
9 comments, last by Peter Svensson 23 years, 9 months ago
Hello everyone! I''m currently making a program in C++ and wonder how errors in a constructor are supposed to be handeld? For exsample: If we run out of memory in the MyClass constructor when trying to allocate memory for the text, should i do like this or what? // A constructor... MyClass::MyClass() { text = new char [100]; if (!text) throw; } SomeClass::SomeMethod() { try { MyClass *myClass = new MyClass(); }catch { delete myClass; myClass = NULL; } } Is this a good way of handling it? Are there other ways? Suggestions, comments ??? // Peter, thanx in advance!
Advertisement
throw an exception

..
Yes Peter, this is the way to do this

I''m not an expert at this, but I think you don''t have to delete the object if an exception occurred in the constructor.

So, if you''d ask me - drop the delete!

Anyone correct me if I''m wrong...

If you''re against exception handling (for whatever reason -- I don''t want to start any religious war) then you can break out your allocation into another function and handle errors returned from it. Eg:
MyClass::MyClass() {};BOOL MyClass::Init(int nSize){    m_psz = new char[nSize];    return (m_psz != NULL);} 

Then you don''t need to have try/catch blocks everywhere, but you need to be SURE to "Init" every instance of your class before you use it. Eg:
{    MyClass *pfoo = new MyClass;    if (NULL == pfoo || !pfoo->Init(30)) {                return FALSE;    }} 

One final thing. I''m sure that we both simplified our code to make things clearer, but both of our examples should have been done without dynamic memory allocation.
In your example, "new char[100]" should be replaced by a member of type "char[100]", since the 100 is known by the class, and there''s no reason to get it from the heap if we can do it statically (since we already know it).
My example is different since the class doesn''t know the size -- it''s passed into the "Init" function. However, the size is still known at compile time (hard-coded to 30) so it would be better done with a template.
Unless you''re getting the size from some input source (the user, a file, or an uncontroled parameter somewhere), then there''s a good chance that you can avoid dynamic memory allocation inside of your class.

...Syzygy
There is frankly little point in testing for new cause an std bad alloc exception will be thrown if new fails (unless you specified another using _set_se_translator)..

..
I noticed that in MFC they are quite fond of simple constructors, and then init() functions (doInitialization?). But as far as I''m concerned, c++ constructors were added to remove the need to remember calling an init-function. Handling constructor errors can be tedious, but rather that than returning to init-functions. If you''ve read deep c++ (a column you''ll find in the msdn-library) you''ll know what I mean.
A polar bear is a rectangular bear after a coordinate transform.
First, you don't need try/catch blocks all over the place. That's the point of C++ exception handling - when you throw an exception, it just keeps unwinding the stack until it gets to an appropriate catch block, even if it must go all the way back to main.

Example:


#include &ltiostream>

using namespace std;

void do_something();
void do_something_else();

void do_something()
{
do_something_else();
}

void do_something_else()
{
cout << "the exception was thrown" << endl;
throw false;
}

int main()
{
try {
do_something();
}

catch(...)
{
cout << "the exception was caught" << endl;
}

return 0;
}



Second, you cannot delete stack-allocated objects. It's very messy, and triggers exceptions all over the place.

This is stack allocation:


Class myobject;



This is heap allocation:


Class* pobject = new Class;



If you call delete on a stack allocated object (i.e., delete this; where "this" is an object on the stack), well let's just say you've got problems when the object goes out of scope.

Objects that throw an exception from their constructors are automatically cleaned up.

Third, you may need to allocate a set amount of memory on the heap if you will be using a lot of those objects (like a large array). Only so much stack memory can be in use at one time (1MB per thread in Win32) and once it runs out, your program crashes.

Fourth, they are referring to the STL overloaded new operator which throws an exception. VC 6.0 SP2 still does not properly support that overload, and you will get errors and crashes when trying to use it. So you must still use the strange check for NULL when using new(). (silly, I know, but it's a problem)




- null_pointer
Sabre Multimedia


Edited by - null_pointer on July 18, 2000 6:23:02 PM
quote:Original post by null_pointer
So you must still use the strange check for NULL when using new(). (silly, I know, but it''s a problem)


You can use _set_new_handler to throw your own exception class to eliminate the messy NULL test..

The only thing to worry is - will it always get thrown? What happens if the memory is so low that it cannot be allocate the exception object?..

..
Hmm...I spent a bit of time trying to find the source for the STL operator new, but it must be on the CD. Well, I wrote this version and it seems to run just fine:


#include &ltnew>
#include &ltiostream>

void* operator new (size_t bytes)
{
void* p = malloc(bytes);

if(!p)
throw bad_alloc(); // might as well use it
else
return p;
}

void main()
{
try {
int* p = new int[4545834589];
delete[] p;
}

catch( bad_alloc& exception ) {
cout << "the exception was caught" endl;
}
}




- null_pointer
Sabre Multimedia
Exceptions are the cleanest way but you really have to design your code with them in mind. Trying to shoehorn exceptions into an app that doesn''t play nice with them will get you into trouble.

I detest post-creation Init functions. You *will* forget to call them. My favorite method is to use a static Create member function, e.g.:

class Foo
{
protected:
Foo(); // Don''t make the contructor public
public:
static Foo * Create()
{
Foo *ret = new Foo;
if (NULL != ret)
// do any initilization
return ret;
}
};

The only problem with this method is that you can create arrays of Foo''s or have embedded Foo''s - you can only ever use pointers to Foo''s. I generally don''t find that to be a big problem though. The advantage is that you will never forgot to initialize properly and you can test for creation failure.

This topic is closed to new replies.

Advertisement