gysbert1
asked on
"new" and C++ exceptions in class constructors
How do I get around this without overloading the new operator on the class.
I have a basic C++ class that has storage and behaviour. As it is a factory class it needs to hit a database upon construction. Problem is that this can of course fail and the database library uses exceptions!
If I am not calling new everything is fine but if I use new to do dynamic allocation on the heap I run into the following scenario:
p = new MyClass(i);
1) Local storage is allocated on the heap.
2) Constructor is called with i as parameter.
3) Constructor fails with an exception which I catch.
All seems well except that the class local storage is never freed and the assignment to p is never made so I cannot free it either!
I know that I can get around this by overloading the new operator, creating a default constructor that only allocates space and then calling that and after that ...
It just seems to me that there must be a better way ?
I have a basic C++ class that has storage and behaviour. As it is a factory class it needs to hit a database upon construction. Problem is that this can of course fail and the database library uses exceptions!
If I am not calling new everything is fine but if I use new to do dynamic allocation on the heap I run into the following scenario:
p = new MyClass(i);
1) Local storage is allocated on the heap.
2) Constructor is called with i as parameter.
3) Constructor fails with an exception which I catch.
All seems well except that the class local storage is never freed and the assignment to p is never made so I cannot free it either!
I know that I can get around this by overloading the new operator, creating a default constructor that only allocates space and then calling that and after that ...
It just seems to me that there must be a better way ?
Let's consider a simple case:
struct X
{
X() { throw 0; }
};
Now, a trivial usage will cause a memory leak:
X* p = new X;
// do something...
delete p;
However, it can be overcome by:
X* p = 0;
try {
p = new X;
// do something
}
catch (...) {
// do something
}
delete p;
struct X
{
X() { throw 0; }
};
Now, a trivial usage will cause a memory leak:
X* p = new X;
// do something...
delete p;
However, it can be overcome by:
X* p = 0;
try {
p = new X;
// do something
}
catch (...) {
// do something
}
delete p;
If new fails from an exception memory should be freed automatically. But basically alexo has a point:
Y* y = new Y;
X* x = new X(1);
...
delete y;
If an exception is thrown during construction of the X object, the memory allocated to y is not released (the memory allocated to the X object is, I think, freed).
alexo painted one method:
Y* y = new Y;
X* x = 0;
try{ x = new X(1); }
catch(...){}
...
delete y;
another way is to use auto_ptr
auto_ptr<Y> y(new Y);
X* x = new X(1);
...
// auto_ptr always deletes the memory allocated to y.
Y* y = new Y;
X* x = new X(1);
...
delete y;
If an exception is thrown during construction of the X object, the memory allocated to y is not released (the memory allocated to the X object is, I think, freed).
alexo painted one method:
Y* y = new Y;
X* x = 0;
try{ x = new X(1); }
catch(...){}
...
delete y;
another way is to use auto_ptr
auto_ptr<Y> y(new Y);
X* x = new X(1);
...
// auto_ptr always deletes the memory allocated to y.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Looking at the question again, gysbert1, how do you know that there is memory that is not freed?
Why don't you catch the exception int the constructor? then you can add a boolean m_isGood to the class and set it to true or false depending on the success of construction. You may add more members to get the details of error if needed.
Hmm.. just thought of something, can't he call "delete this;" from constructor?
what will happen then ?
what will happen then ?
{
MyClass m;
}
MyClass m;
}
As mentioned, the problem is not the memory allocated for the MyClass object. Leaks may arise from objects that have been constructed during construction of the MyClass object. You may think you did a good job by releasing resources in MyClass destructor, but unfortunately, the MyClass destructor i not called if construction was not complete!
ASKER
A few final comments
dhymes: NO
The following class allocates memory when you call new and ilustrates my problem.
class myClass
{
int i;
myClass() {throw i;};
};
alexo:
Thanks for the quote from the spec! That lead me to the answer!
After more detailed analysis it turned out that it was not leaking in the way I thought!
The problem was that a member in my exception class was a STL string and that damn thing leaks !!!
It only leaks on the Borland compiler and not on Visual Age which I am also using so we have identified a compiler (or rather STL implementation) bug.
Here is my test code that leaked for reference:
#include <iostream>
class tEX
{
public:
std::string error;
tEX(std::string msg) {error = msg;};
tEX(const tEX& e) {error = e.error;};
};
class myClass
{
public:
char buffer[1024];
myClass() {throw tEX("error !!");};
};
void main(void)
{
myClass* p = NULL;
int i = 0;
while (i++ < 10000)
{
try
{
p = new myClass;
}
catch (...) {}
delete p;
}
}
dhymes: NO
The following class allocates memory when you call new and ilustrates my problem.
class myClass
{
int i;
myClass() {throw i;};
};
alexo:
Thanks for the quote from the spec! That lead me to the answer!
After more detailed analysis it turned out that it was not leaking in the way I thought!
The problem was that a member in my exception class was a STL string and that damn thing leaks !!!
It only leaks on the Borland compiler and not on Visual Age which I am also using so we have identified a compiler (or rather STL implementation) bug.
Here is my test code that leaked for reference:
#include <iostream>
class tEX
{
public:
std::string error;
tEX(std::string msg) {error = msg;};
tEX(const tEX& e) {error = e.error;};
};
class myClass
{
public:
char buffer[1024];
myClass() {throw tEX("error !!");};
};
void main(void)
{
myClass* p = NULL;
int i = 0;
while (i++ < 10000)
{
try
{
p = new myClass;
}
catch (...) {}
delete p;
}
}
Sorry, you just hit a soft spot with me. Allocation of memory inside of a constructor should always be avoided. If you can not avoid it then you need to take another look at your application architecture.
Regards,
Dave