ElementDeleter.h
Go to the documentation of this file.
/* Frobby: Software for monomial ideal computations.
Copyright (C) 2007 Bjarke Hammersholt Roune (www.broune.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
*/
#ifndef ELEMENT_DELETER_GUARD
#define ELEMENT_DELETER_GUARD
// The purpose of this class is to call delete on the elements of a
// standard library container which holds pointers. It removes the
// elements from the container as they are being deleted, but it does
// not delete the container itself. Do not use this class to
// deallocate a vector of built-in arrays, since those require
// deallocation using delete[] rather than delete.
//
// This can be a convenience when coding, but the real purpose is to
// avoid memory leaks in the face of an exception being thrown. When a
// standard library container goes out of scope, its destructor is
// called, but the destructor does not deallocate pointer elements, so
// they will leak unless special care is taken to aovid that, such as
// using this class.
//
// The most straight-forward solution may seem to use
// e.g. vector<unique_ptr<T>> in place of vector<T*>, but this does not
// work because storing unique_ptr<T> in a standard library container is
// never safe (look this up on the internet to get the details).
//
// The reference counting smart pointer class shared_ptr from Boost or
// TR1 would solve this issue even better by using
// vector<shared_ptr<T>> instead of vector<T*>, but that would require
// a dependency on Boost or on having a very recent compiler which
// incorporates TR1.
//
// TODO: make an AutoVector that wraps std::vector instead of or as a
// supplement to this class.
//
// Container::value_type is required to be a pointer type.
template<class Container>
class ElementDeleter {
public:
// Only the constructor can attach an ElementDeleter to a container,
// and this helps ensure (but does not guarantee) the precondition
// of the destructor of ElementDeleter.
ElementDeleter(Container& container): _container(&container) {
}
// Calls deleteElements() and shares its precondition.
~ElementDeleter() {
deleteElements();
}
// Call release() to prevent this ElementDeleter from manipulating
// the container in any way.
void release() {
_container = 0;
}
// If release has been called, this method does nothing. Otherwise
// the container must still be valid, and then delete is called on
// each element of the array and clear is called on the container.
void deleteElements() {
if (_container == 0)
return;
// The code below may seem obviously correct, but it is a
// non-trivial fact that it works in the face of exceptions.
//
// First of all, we are allowed to assume that destructors do not
// throw exceptions, so the loop will run to completion.
//
// Normally clear() *can* throw an exception according to the
// standard (which is weird), but if the copy constructor and
// assignment operator do not throw exceptions, then it does
// not. In our case, we require the element type to be a pointer
// type, so for this reason only do we know that clear will not
// throw an exception. Thus we do not have to worry about leaving
// around a container full of invalid pointers if clear() should
// throw an exception.
typename Container::iterator end = _container->end();
typename Container::iterator it = _container->begin();
for (; it != end; ++it)
delete *it;
_container->clear();
}
private:
Container* _container;
};
// exceptionSafePushBack is designed to work around the fact that this
// code can leak memory:
//
// vector<int*> vec;
// ElementDeleter<vector<int*> > elementDeleter(vec);
// unique_ptr<int> p(new int());
// vec.push_back(p.release())
//
// This is because push_back can fail by throwing a bad_alloc, and at
// that point the pointer in p has already been released, so that
// pointer is now lost and hence the memory is leaked.
//
// This can be fixed by replacing
//
// vec.push_back(p.release())
//
// by
//
// vec.push_back(0);
// vec.back() = p.release();
//
// but this is annoying and looks quite strange. It is much clearer to
// write
//
// exceptionSafePushBack(vec, p);
//
template<class Container, class Element>
void exceptionSafePushBack(Container& container, unique_ptr<Element> pointer) {
container.push_back(0);
container.back() = pointer.release();
}
// Serves the same purpose as exceptionSafePushBack, except that this
// overload will simply ignore an exception without propagating it.
// Thus if there is not enough memory to add the element to the container,
// the element will simply get deleted and the container will remain
// unchanged. This works well if e.g. adding an element to a cache, where
// it is no great problem if the element gets deleted rather than added.
template<class Container, class Element>
void noThrowPushBack(Container& container, unique_ptr<Element> pointer) throw () {
try {
exceptionSafePushBack(container, std::move(pointer));
} catch (const bad_alloc&) {
// Ignore the exception.
}
}
#endif
exceptionSafePushBack
void exceptionSafePushBack(Container &container, unique_ptr< Element > pointer)
Definition ElementDeleter.h:129
noThrowPushBack
void noThrowPushBack(Container &container, unique_ptr< Element > pointer)
Definition ElementDeleter.h:141
ElementDeleter::deleteElements
void deleteElements()
Definition ElementDeleter.h:72
ElementDeleter::release
void release()
Definition ElementDeleter.h:65
ElementDeleter::ElementDeleter
ElementDeleter(Container &container)
Definition ElementDeleter.h:55
ElementDeleter::~ElementDeleter
~ElementDeleter()
Definition ElementDeleter.h:59
ElementDeleter::_container
Container * _container
Definition ElementDeleter.h:99