Re: Possible memory leak when constructing an object from a new expression?
On 2011-04-19 01:50, Matthias Hofmann wrote:
Hello everybody!
[..]
The standard does in fact guarantee that if TBar's constructor throws an
exception, the memory allocated for the object will be freed. If operator
new fails and throws std::bad_alloc before calling TBar's constructor, we do
not have a problem either. But what if, and that is my question,
std::auto_ptr throws an exception? Will the memory allocated for the new
TBar object be freed, maybe because it is part of the same expression, or
not?
*If* the constructor of std::auto_ptr could throw an exception, it should better guarantee that this would still ensure that the accepted resource is freed. See below for guarantees of std::auto_ptr and see further below for an existing example (std::shared_ptr), which *can* throw an exception.
I don't know what the standard says about std::auto_ptr throwing an
exception,
The corresponding operation of std::auto_ptr is a guaranteed non-throwing as of [auto.ptr]:
explicit auto_ptr(X* p =0) throw();
which is an important guarantee for the end user.
but you might as well take the following example:
template<class T> class X
{
T* m_ptr;
public:
X( T* ptr ) : m_ptr( ptr )
{
// Just for the fun of it.
throw 0;
}
};
int main()
{
X<int> x( new int );
return 0;
}
What happens with the memory allocated for the int? Will it be freed or not?
There is no delete expression in your code, so how could the memory be freed? If you intended to provide a destructor of X that calls delete on m_ptr, this will never be c alled, because a complete X object was never constructed.
Before answering *how* to realize this, let me give you an example of a new library component, std::shared_ptr, which has a constructor taking a resource,
template<class Y> explicit shared_ptr(Y* p);
that might throw an exception in special situations. From the FDIS document, [util.smartptr.shared.const] p. 6+7:
<quote>
6 Throws: bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.
7 Exception safety: If an exception is thrown, delete p is called.
</quote>
If your resource-holder cannot give such a guarantee, it is unusable.
To realize that, there are different choices. E.g. for your class template X you could solve it this way:
template <class T> class X
{
T* m_ptr;
public:
X( T* ptr ) : m_ptr( )
{
std::auto_ptr tmp(ptr);
{
//.. potentially throwing code
// Just for the fun of it.
throw 0;
}
// OK, the potentially throwing block succeeded, so we can
// now transfer the resource to the actual holder:
m_ptr = tmp. release();
}
};
Another alternative to solve this issue is to provide a member or base-class that guarantees a no-throw resource acceptance, because the standard guarantees that every completely constructed sub-object will be destructed, if the constructor of the corresponding "container" object throws an exception. E.g. you could decide to use a std::auto_ptr as e member in X:
template <class T> class X
{
std::auto_ptr<T> m_ptr;
public:
X( T* ptr ) : m_ptr(ptr)
{
// Just for the fun of it.
throw 0;
}
};
This is an exception-safe class, because the corresponding constuctor of std::auto_ptr will guarantee that the initialization of std::auto_ptr will never throw an exception (see above), but at the point were the exception is thrown within the X<> constructor, the construction of the sub-object m_ptr is completed, thus the destructor of that sub_object will be invoked before leaving the X constructor.
I just took a look at the assembly code that Visual C++ 2005 generates in
this case and found no indication that the memory gets freed, but what does
the standard say about this?
The standard says that in the X example no resource is freed if you don't ensure this properly as shown above.
If memory deallocation is not guaranteed in
this case, is there at least any guarantee that std::auto_ptr' constructor
does not throw, so that the implementation of the copy assignment operator
in the article I found is still correct?
The standard library provides the nothrow guarantee for all operations of std::auto_ptr.
I recommend to read follow-up literature, e.g.
"Exceptional C++" from Herb Sutter
or some of his articles, like
http://preview.tinyurl.com/444nf2u
http://www.gotw.ca/publications/using_auto_ptr_effectively.htm
http://www.gotw.ca/gotw/066.htm
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]