Archived

This topic is now archived and is closed to further replies.

A good way of handling errors from a constructor?

This topic is 6361 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites
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...

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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)..

..

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 <iostream>

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

Share this post


Link to post
Share on other sites
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?..

..

Share this post


Link to post
Share on other sites
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 <new>
#include <iostream>

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

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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.

Share this post


Link to post
Share on other sites
quote:

they are referring to the STL overloaded new operator which throws an exception. VC 6.0 SP2 still does not properly support that overload



STL doesn''t have its own operator new (STL is the collective name for the containers, algorithms, iterators & function objects (did I miss anything?)), it''s the standard C++ operator new that throws std::bad_alloc, so you probably wont find the source code it (the compiler should do it by default). new in MSVC isn''t ''correct'' because it still returns NULL (but new in MFC throws CMemoryException).

If your going to write your own operator new to use malloc, you should do the same for delete to call free. An easier would be to leave the current new and delete alone, and write your own new handler (it''ll be called if new fails). e.g.


#include

int my_new_handler(size_t size)
{
throw std::bad_alloc();
}

int main(int argc, char* argv[])
{
_set_new_handler(my_new_handler);

//now if new fails it''ll throw std::bad_alloc

return 0;
}

Share this post


Link to post
Share on other sites