borland c++ compiler won't undo memory allocation when exception thrown across constructor boundary?
I am using bcc32 command line compiler from Borland Embarcadero. Consider this program:
int main(int, char **)
{
try
{
std::string *a = new string(0xf0000000, ' ');
...
delete a;
}
catch(const std::bad_alloc &)
{
...
}
}
When the std::string constructor throws a memory exception, the stack is unwound and control is passed to the catch-block. Gnu compilers build in code to delete the memory allocated for the std::string object 'auto-magically', as was stated by someone who commented on the answer in Who deletes the memory allocated during a "new" operation which has exception in constructor? which I wrote. I ran the program in http://ideone.com/IRxHX and the result is that nobody frees the memory allocated by 'operator new' if an exception is thrown before the result of 'new' is stored in an lvalue. In the above case the variable 'a'.
Questions are: 1 Is there a way 开发者_如何学Cto delete the memory generated by 'new' in case of an exception, as a part of the stack unwind procedure? 2 What does the C++ standard demand from compilers in this case
Either your compiler is broken, or something else funny is going on. The implementation is required to free the memory:
5.3.4/17:
If any part of the object initialization described above terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new- expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object’s memory to be freed.
I tested your sample code with the following, stepping through the code to make sure it all works fine (I took your QC code and modified it to make it work as intended):
#include <tchar.h>
#include <string>
#include <iostream>
int count = 0;
bool start = false;
// from your QC code, modified to count instead of std::cout
void *operator new(size_t cbytes)
{
void *retval = std::malloc(cbytes);
if (retval == NULL && cbytes != 0) throw std::bad_alloc();
if (start) count++;
return retval;
}
// from your QC code, modified to count instead of std::cout
void operator delete(void *block)
{
if (block != NULL)
{
std::free(block);
if (start) count--;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
try
{
start = true;
std::string *a = new std::string(0xf0000000, ' ');
// ....
delete a;
}
catch(const std::bad_alloc&)
{
}
std::cout << count;
return 0;
}
I get 0
for count
indicating that the partially constructed string does get its memory freed. Tested with MSVC++ 2005 / 2010 with the same results.
Tested with C++ Builder 2010 (command line: bcc32 program.cpp
). It is consistent with the C++ standards.
EDIT: Ah, finally saw that your QC report specifies the usage of dynamic RTL with the command line bcc32 -WCR program.cpp
. And yes, when compiling with dynamic RTL, I see the problem. Even then, my test case would've been much better at show casing the problem.
The C++ standard demands that the delete will be called by the compiler.
The C+ standard section which addresses this is:
15.2 Constructors and destructors
1 As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.
2 An object that is partially constructed or partially destroyed will have destructors executed for all of its fully constructed base classes and non-variant members, that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
3 The process of calling destructors for automatic objects constructed on the path from a try block to a throwexpression is called “stack unwinding.” [ Note: If a destructor called during stack unwinding exits with an exception, std::terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note ]
精彩评论