Exception Help please...
Okay, so I looked at the code generated when adding exception handling to my project...
a try block consists of exactly one mov and one jmp... cool.
So I added exception handling to everything, and now I think I have a problem.
Lets say I call new to create a new instance of a class, (an encapsulated array of my own design), and suppose when the array class is constructing, it causes an exception when it tries to allocate too much memory.
What happens? Does the array class get destructed then? or is there a memory leak (even though the internal array was not created, what about the encapsulated array class)?
i''ve traced through it, the first call to new malloc''s memory for the array class (about 16 bytes), then calls the constructor, which calls new again to create the internal array, then the program either throws an exception if new returned 0, or catches a bad_alloc exception and rethrows it as one of my own, then the execution jumps out of the constructor and down to the catch clause outside the array class. I can''t tell if it dealloc''d the memory (16 bytes) for the array class.
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
This is my signature. There are many like it, but this one is mine. My signature is my best friend. It is my life. I must master it as I must master my life. My signature, without me, is useless. Without my signature, I am useless.
when an exception is thrown during the creation of an object, the object is not created, but any heap memory allocated by new, malloc is not freed until you do it yourself.
Go read guru of week and check the articles about exception safety. You''ll then know everthing you need to make your classes exception safe.
http://www.gotw.ca/gotw/
Enjoy!
Go read guru of week and check the articles about exception safety. You''ll then know everthing you need to make your classes exception safe.
http://www.gotw.ca/gotw/
Enjoy!
How does this sound: (slightly related)
changing the new exception handler to throw exceptions of my own design, rather than bad_alloc?
I do this for a reason, my exception classes are far more robust and contain a lot more important debug info, and I really don''t want to re-throw a bad_alloc exception every time new fails.
my C++ book says that set_new_handler should only be used to rethrow bad_alloc though =(
my other option would be to ignore new failures... heh.
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
changing the new exception handler to throw exceptions of my own design, rather than bad_alloc?
I do this for a reason, my exception classes are far more robust and contain a lot more important debug info, and I really don''t want to re-throw a bad_alloc exception every time new fails.
my C++ book says that set_new_handler should only be used to rethrow bad_alloc though =(
my other option would be to ignore new failures... heh.
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
This is my signature. There are many like it, but this one is mine. My signature is my best friend. It is my life. I must master it as I must master my life. My signature, without me, is useless. Without my signature, I am useless.
quote:
Original post by Gorg
when an exception is thrown during the creation of an object, the object is not created, but any heap memory allocated by new, malloc is not freed until you do it yourself.
Just to be more explicit:
When a ctor for an object fails, the memory for that object only (i.e. the sizeof the class whose ctor you''re calling) is automatically released.
However, if any dynamic allocation occurred within the ctor, it must be manually released.
I only bring it up because when I read your post really quickly, I thought you said something else (which you didn''t, you''re right).
Mith: I can understand you not wanting to rethrow exceptions from ctors just because it looks a little messy, but creating a new new function and moving the code there might, IMHO, obfuscate the code. Also, doesn''t new have to be a free function, or can it be a member function? I''ve never overloaded new myself, so I don''t know. I thought it had to be free, and in that case you wouldn''t have access to the member variables you need to release anyway.
So if I''m reading you right, you''re doing something like this:
|
I think this should be fine, if a little messy. Might also want a catch (...) at the end. But what are you doing with this exception? If any ctor fails, does your program just spit a message and exit? If so, you might be fine removing this check from within the ctor and just catch it on the outside, especially for out-of-memory errors (once you start getting those, your program is NOT going to work in all probability.)
er, imagine a slight addition:
well, i am developing a series of low level libraries which should alert the client whenever any of the classes cannot be constructed, or any operations that should fail.
I''m designing this system so that it throws exceptions when something goes really wrong and the classes have no idea how to handle it.
About the GOTW articles... the examples they give in #66, about catching base class exceptions don''t work.
VC6 gives me compiler errors when I try to do this (inline implementation, since I''m using templates... does it matter?):
the article shows code which looks like this:
I just want to know if this is standard?
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
|
quote:
I think this should be fine, if a little messy. Might also want a catch (...) at the end. But what are you doing with this exception? If any ctor fails, does your program just spit a message and exit? If so, you might be fine removing this check from within the ctor and just catch it on the outside, especially for out-of-memory errors (once you start getting those, your program is NOT going to work in all probability.)
well, i am developing a series of low level libraries which should alert the client whenever any of the classes cannot be constructed, or any operations that should fail.
I''m designing this system so that it throws exceptions when something goes really wrong and the classes have no idea how to handle it.
About the GOTW articles... the examples they give in #66, about catching base class exceptions don''t work.
VC6 gives me compiler errors when I try to do this (inline implementation, since I''m using templates... does it matter?):
|
the article shows code which looks like this:
|
I just want to know if this is standard?
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
This is my signature. There are many like it, but this one is mine. My signature is my best friend. It is my life. I must master it as I must master my life. My signature, without me, is useless. Without my signature, I am useless.
Well, i''ve found some doc''s on the subject (every C++ book i have looked through is completely useless) on MSDN, and here is what I''ve found out:
My initial fears are correct. A call to ''new'' is dissemminated into these logical calls by the assember:
In my example, the first malloc works correctly. Then the constructor is called, but the problem is that the constructor throws an exception. This causes execution to immediately jump out of the constructor, jump past the return command, and search for the appropriate catch block, essentially losing the memory.
If i remember correctly, there is a little used feature called auto_ptr which points to the last memory block allocated by new. However, this strikes me as a particularly clunky way to handle the problem, and I have heard horror stories about this and other exception features not working correctly on some of the more popular compilers, so this is not a viable solution.
I think I shall revise my official policy to "If the user is causing my classes to throw exceptions on creation, they are fucked anyway, and deserve to have a memory leak".
At first I thought this would have major ramifications on memory leaks, but with a little strictness, the exceptions are only meant to handle things which absolutely cannot happen in code, and pass it up to code that others can fix.
usually that only happens when debugging, so I don''t have to worry about code barfing in production mode. Any exceptions that occur in production mode will try to save important state data, display an error, and quit.
Also very bad is throwing in a destructor, because delete follows this pattern:
again, an exception will skip over the dealloc call.
sigh.
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
My initial fears are correct. A call to ''new'' is dissemminated into these logical calls by the assember:
|
In my example, the first malloc works correctly. Then the constructor is called, but the problem is that the constructor throws an exception. This causes execution to immediately jump out of the constructor, jump past the return command, and search for the appropriate catch block, essentially losing the memory.
If i remember correctly, there is a little used feature called auto_ptr which points to the last memory block allocated by new. However, this strikes me as a particularly clunky way to handle the problem, and I have heard horror stories about this and other exception features not working correctly on some of the more popular compilers, so this is not a viable solution.
I think I shall revise my official policy to "If the user is causing my classes to throw exceptions on creation, they are fucked anyway, and deserve to have a memory leak".
At first I thought this would have major ramifications on memory leaks, but with a little strictness, the exceptions are only meant to handle things which absolutely cannot happen in code, and pass it up to code that others can fix.
usually that only happens when debugging, so I don''t have to worry about code barfing in production mode. Any exceptions that occur in production mode will try to save important state data, display an error, and quit.
Also very bad is throwing in a destructor, because delete follows this pattern:
|
again, an exception will skip over the dealloc call.
sigh.
===============================================
Have I no control, is my soul not mine?
Am I not just man, destiny defined?
Never to be ruled, nor held to heel!
This is my signature. There are many like it, but this one is mine. My signature is my best friend. It is my life. I must master it as I must master my life. My signature, without me, is useless. Without my signature, I am useless.
May 02, 2001 02:04 PM
It is not safe to do anything in a constructor//destructor that can fail(Think that is one of the things because they don´t return any type...).
You should keep your constructors//destructors JUST do:
-Init variables.(Constructors).
-Call other functions that do the job.
In a constructor just set your variables and call Init().This is a better design.In destructors just call Clean().
Search for the Factory pattern over the net.There is something in www.Flipcode.com, in one of the author´s columns.
Do yourself a favour and buy this book: "Dessign Patterns", Addison-Wesley,Erich Gamma...
Game Programming Gems also has a gem about patterns...
Hope to help!
What the hells!
You should keep your constructors//destructors JUST do:
-Init variables.(Constructors).
-Call other functions that do the job.
In a constructor just set your variables and call Init().This is a better design.In destructors just call Clean().
Search for the Factory pattern over the net.There is something in www.Flipcode.com, in one of the author´s columns.
Do yourself a favour and buy this book: "Dessign Patterns", Addison-Wesley,Erich Gamma...
Game Programming Gems also has a gem about patterns...
Hope to help!
What the hells!
hm... a very long one.... let's see.......
Now let's just look into the constructor:
Default case :
Everything run as expected. No exception is thrown.
![](smile.gif)
Case 1 :
If somedata1 have allocation failure, exception will be thrown, leaving the following line un-executed (somedata2 still unalloc). Thus the entire object is not properly construct, so no object is created. And hence destructor also don't get executed. But what if.....
Case 2 :
Consider somedata1 has successfully alloc. But then somedata2 somehow don't feel nice and throw an exception. What happen then? The object is still not fully created and thus no destructor will be called. BUT, however, take NOTE : somedata1 is still allocated. IT IS NOT FREED!! Memory leaks!![](sad.gif)
In your main() , following the cases above:
Default case : Both a1 and a2 objects are created. Both's destructor is called when a1 goes out-of-scope and when a2 is delete -ed.
Case 1 : Both a1 and a2 are NOT created. You have no needs to free a2.
Case 2 : Same as Case1. But with little memory leak inside the constructor.
Another thing, if in the middle of the main() some functions throw s again, your a2 will not be freed (unless u put delete in every catch )
To deal with the leak, you could use smart pointers... or auto_ptr (under STL). Let see a better one:
And now, when your somedata2 really 'throw', then your somedata1 will be freed automatically. So is a2.
Got the idea??![](smile.gif)
Hope this helps.![](wink.gif)
Edited by - DerekSaw on May 2, 2001 3:23:19 PM
class A{public: A() { somedata1 = new something1; // might throw if fail somedata2 = new something2; // here too } ~A() { delete somedata1; delete somedata2; } ....};int main(){ try { A a1, *a2 = new A; ... delete a2; } catch(...) { ... } return 0;}
Now let's just look into the constructor:
Default case :
Everything run as expected. No exception is thrown.
![](smile.gif)
![](smile.gif)
Case 1 :
If somedata1 have allocation failure, exception will be thrown, leaving the following line un-executed (somedata2 still unalloc). Thus the entire object is not properly construct, so no object is created. And hence destructor also don't get executed. But what if.....
Case 2 :
Consider somedata1 has successfully alloc. But then somedata2 somehow don't feel nice and throw an exception. What happen then? The object is still not fully created and thus no destructor will be called. BUT, however, take NOTE : somedata1 is still allocated. IT IS NOT FREED!! Memory leaks!
![](sad.gif)
In your main() , following the cases above:
Default case : Both a1 and a2 objects are created. Both's destructor is called when a1 goes out-of-scope and when a2 is delete -ed.
Case 1 : Both a1 and a2 are NOT created. You have no needs to free a2.
Case 2 : Same as Case1. But with little memory leak inside the constructor.
Another thing, if in the middle of the main() some functions throw s again, your a2 will not be freed (unless u put delete in every catch )
To deal with the leak, you could use smart pointers... or auto_ptr (under STL). Let see a better one:
class A{public: A() { auto_ptr < something1 > somedata1( new something1 ); // might throw exception auto_ptr < something2 > somedata2( new something2 ); // same to here too } ~A() { // since auto_ptr will free itself. no delete is require } ....};int main(){ try { A a1; auto_ptr < A > a2( new A ); ... // again, no delete is require. } catch(...) { ... } return 0;}
And now, when your somedata2 really 'throw', then your somedata1 will be freed automatically. So is a2.
Got the idea??
![](smile.gif)
Hope this helps.
![](wink.gif)
Edited by - DerekSaw on May 2, 2001 3:23:19 PM
"after many years of singularity, i'm still searching on the event horizon"
Oops... I forgot u r dealing array. ![](wink.gif)
Here we go again:
If u allocate arrays by:
then there should be no problem.
If u allocate something like this (or similar):
then you should take note.
What method u use to allocate, u must also do the same when deallocation.![](wink.gif)
![](wink.gif)
Here we go again:
If u allocate arrays by:
A *arr = new A[100];
then there should be no problem.
If u allocate something like this (or similar):
A *arr[100];for (int j = 0; j < 100; j++) arr[j] = new A;
then you should take note.
What method u use to allocate, u must also do the same when deallocation.
![](wink.gif)
"after many years of singularity, i'm still searching on the event horizon"
Memory allocated to an object whose ctor fails is automatically deallocated. Here's a test program in MFC (so I can use MFC's nifty DEBUG_NEW leak-tracking option):
Running the program produces this trace:
So the 667 bytes that I intentionally leaked are in fact leaked, but the Leaker object, whose sizeof should be 4096, was not leaked. Everything's fine. C++ will do the right thing when the ctor fails. The only thing you have to be careful of is allocations _within_ a ctor that could then fail.
Regarding exception inheritence: The scheme I use is to derive all of my custom exceptions from std::exception. This gives me a method to get an error string (what ()) and I usually only need that so I only have one case.
When using inheritence with exceptions derived from std::exception, you need to catch references to exceptions, not exception objects. This is the only way to make the call to what () call the appropriate function in the vtable. That's why I did a catch (exception &x) instead of catch (exception x). However, if you're going to re-throw an exception, you have to create a new object--not really sure the reason, but this is what I've discovered through trial & error. Example:
I think if you try to re-throw the object, the object has been destroyed by the time you get to the outer catch. I think this is because you create the runtime_error in the try scope (which I guess extends to its related catch scope), so it's destroyed before it gets to the outer loop.
I have a lot of experience with exceptions, so please let me know if you have any questions or need any more specific examples. Good luck.
Also note--MFC's exception (CException, etc) are completely different and should be avoided in my humble opinion . They require you to catch pointers to exceptions, and I believe require you to delete the exception object. I would also avoid SEH since it's windows-specific and will not port.
Edited by - Stoffel on May 2, 2001 4:39:36 PM
// MfcTest.cpp : Defines the class behaviors for the application.//#include "stdafx.h"#include "process.h"#include "MfcTest.h"#include "MfcTestDlg.h"#include using namespace std;#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endifclass Leaker{ char mem[4096]; // nice big block o memorypublic: Leaker () { throw runtime_error ("ctor failed"); }};/////////////////////////////////////////////////////////////////////////////// CMfcTestAppBEGIN_MESSAGE_MAP(CMfcTestApp, CWinApp) //{{AFX_MSG_MAP(CMfcTestApp) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG ON_COMMAND(ID_HELP, CWinApp::OnHelp)END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CMfcTestApp constructionCMfcTestApp::CMfcTestApp(){ // TODO: add construction code here, // Place all significant initialization in InitInstance}/////////////////////////////////////////////////////////////////////////////// The one and only CMfcTestApp objectCMfcTestApp theApp;/////////////////////////////////////////////////////////////////////////////// CMfcTestApp initializationBOOL CMfcTestApp::InitInstance(){ AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need.#ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL#else Enable3dControlsStatic(); // Call this when linking to MFC statically#endif Leaker *pLeaker; try { pLeaker = new Leaker; } catch (exception &) { TRACE ("Exception caught\n"); char *pTest = new char[667]; // just to show leak detection works } CMfcTestDlg dlg; m_pMainWnd = &dlg int nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. return FALSE;}
Running the program produces this trace:
Loaded 'C:\WINNT\System32\ntdll.dll', no matching symbolic information found.Loaded symbols for 'C:\WINNT\system32\MFC42D.DLL'Loaded symbols for 'C:\WINNT\system32\MSVCRTD.DLL'Loaded 'C:\WINNT\system32\KERNEL32.DLL', no matching symbolic information found.Loaded 'C:\WINNT\system32\GDI32.DLL', no matching symbolic information found.Loaded 'C:\WINNT\system32\USER32.DLL', no matching symbolic information found.Loaded 'C:\WINNT\system32\ADVAPI32.DLL', no matching symbolic information found.Loaded 'C:\WINNT\system32\RPCRT4.DLL', no matching symbolic information found.Loaded symbols for 'C:\WINNT\system32\MFCO42D.DLL'Loaded symbols for 'C:\WINNT\system32\MSVCP60D.DLL'Exception caughtLoaded 'C:\WINNT\system32\COMCTL32.DLL', no matching symbolic information found.First-chance exception in MfcTest.exe (KERNEL32.DLL): 0xE06D7363: Microsoft C++ Exception.Loaded 'C:\WINNT\system32\msidle.dll', no matching symbolic information found.Detected memory leaks!Dumping objects ->C:\projects\mfctest\MfcTest.cpp(78) : {67} normal block at 0x00301690, 667 bytes long. Data: CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete.The thread 0x16D has exited with code 0 (0x0).The program 'C:\projects\mfctest\Debug\MfcTest.exe' has exited with code 0 (0x0).
So the 667 bytes that I intentionally leaked are in fact leaked, but the Leaker object, whose sizeof should be 4096, was not leaked. Everything's fine. C++ will do the right thing when the ctor fails. The only thing you have to be careful of is allocations _within_ a ctor that could then fail.
Regarding exception inheritence: The scheme I use is to derive all of my custom exceptions from std::exception. This gives me a method to get an error string (what ()) and I usually only need that so I only have one case.
When using inheritence with exceptions derived from std::exception, you need to catch references to exceptions, not exception objects. This is the only way to make the call to what () call the appropriate function in the vtable. That's why I did a catch (exception &x) instead of catch (exception x). However, if you're going to re-throw an exception, you have to create a new object--not really sure the reason, but this is what I've discovered through trial & error. Example:
|
I think if you try to re-throw the object, the object has been destroyed by the time you get to the outer catch. I think this is because you create the runtime_error in the try scope (which I guess extends to its related catch scope), so it's destroyed before it gets to the outer loop.
I have a lot of experience with exceptions, so please let me know if you have any questions or need any more specific examples. Good luck.
Also note--MFC's exception (CException, etc) are completely different and should be avoided in my humble opinion . They require you to catch pointers to exceptions, and I believe require you to delete the exception object. I would also avoid SEH since it's windows-specific and will not port.
Edited by - Stoffel on May 2, 2001 4:39:36 PM
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement