|
Boost : |
From: Michel André (michel.andre_at_[hidden])
Date: 2000-03-09 09:33:36
Hello!
I was a part of the discussion and is currently using my
own definitions of the smart_ptr classes which included
the empty base member optimization and parmetrize the delete/
release operation. It's based on the discussion Greg, Beman,
Howard, Me and several others had late last year. I use it
for general purposes to release Windows NT handles and such.
> > o I wonder, whether it would be useful to parametrize the release
operation,
> > at least for scoped_ptr and scoped_array.
> > Couldn't we implement a template for two types, the element type
and the
> > release operation. This would allow to use the "resource
acquisition is
> > initialization" idiom with other acquisitions than new and new[].
>
> Yes, we have discussed this before and it is on my to-do list.
> Unfortunately I am very busy with my main job these days and
> am not making much progress on that list.
>
> > Attached is an example implementation.
I enclose a copy of my definitions wich I use in one of my projects
currently. It also has an implementetation of the so much discussed
release operation (which fits my purposes but may not fit in a broader
implementation, because its very Q&D).
Regards
/Michel
//*********************************************************************
********//
// Michel André - Exmentis Software
// E-mail: michel.andre_at_[hidden]
//
// (C) Copyright Exmentis Software 2000. Permission to use, modify
this
// software is granted provided this copyright notice appears in all
copies.
// This software is provided "as is" without express or implied
warranty, and
// with no claim as to its suitability for any purpose.
//
// xstl.h Standard template library enhancement
// classes/routines/algorithms
//
// Part (smart pointer family of classes) of the code is based
// on code from www.boost.org.
// It's extended and changed to fit my purposes.
//
// Copyright notice from boost:
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. Permission to
copy,
// use, modify, sell and distribute this software is granted provided
this
// copyright notice appears in all copies. This software is provided
"as is"
// without express or implied warranty, and with no claim as to its
// suitability for any purpose.
//*********************************************************************
******//
// class noncopyable -------------------------------------------------
-------//
// Private copy constructor and copy assignment ensure classes
// derived from
// class noncopyable cannot be copied.
class noncopyable
{
protected:
noncopyable() {}
private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
}; // noncopyable
template<typename T> class deleter_function : public
std::unary_function<T*&,void>
{
public:
};
template<typename T> class deleter : public deleter_function<T>
{
public:
void operator()(T*& p) throw() { delete p; p = NULL; };
};
template<typename T> class array_deleter : public deleter_function<T>
{
public:
void operator()(T*& p) throw() { delete[] p; p = NULL; };
};
// Class that does member base optimization over the
// deleter class. To piggyback an empty class instance
// on an existing member to minimize class size
template <typename Base, typename Member> struct base_opt : Base {
Member m;
base_opt(Base const& b, Member const& m) : Base(b), m(m) {}
base_opt(const base_opt& rhs) throw() : Base(rhs) { m = rhs.m; }
};
template<typename T , typename Deleter> class base_ptr : noncopyable
{
public:
typedef T element_type;
T& operator*() const throw() { return *ptr(); }
T* operator->() const throw() { return ptr(); }
T* get() const throw() { return ptr(); }
protected:
T*& ptr() { return deleter.m; }
T* ptr() const { return deleter.m; }
void del() { deleter(deleter.m); }
void del(T* p) { deleter(p); };
base_ptr(T* p,Deleter d) throw() : deleter(d,p) {};
base_ptr(const base_ptr& rhs) throw() : deleter(rhs.deleter) { }
private:
base_opt< Deleter, T* > deleter;
};
// scoped_ptr ------------------------------------------------------
--------//
// scoped_ptr mimics a built-in pointer except that it guarantees
deletion
// of the object pointed to, either on destruction of the scoped_ptr
or via
// an explicit reset(). scoped_ptr is a simple solution for simple
needs;
// see shared_ptr (below) or std::auto_ptr if your needs are more
complex.
template< typename T , typename Deleter = deleter<T> > class
scoped_ptr : public base_ptr< T , Deleter >
{
public:
typedef T element_type;
explicit scoped_ptr( T* p=0, const Deleter& d = deleter<T>()) throw()
: base_ptr<T,Deleter>(p,d) {}
~scoped_ptr() { reset(); }
void reset( T* p=0 )
{
if ( ptr() != p )
{
del();
ptr() = p;
}
}
}; // scoped_ptr
// scoped_array ----------------------------------------------------
--------//
// scoped_array extends scoped_ptr to arrays. Deletion of the array
pointed to
// is guaranteed, either on destruction of the scoped_array or via an
explicit
// reset(). See shared_array or std::vector if your needs are more
complex.
template< typename T , typename Deleter = array_deleter<T> > class
scoped_array : public scoped_ptr<T,Deleter>
{
public:
typedef T element_type;
explicit scoped_array( T* p=0,const Deleter& d = array_deleter<T>())
throw() : scoped_ptr<T,Deleter>(p,d) {}
~scoped_array() { reset(); }
T& operator[](size_t i) const throw() { return ptr()[i]; }
}; // scoped_array
// shared_ptr ------------------------------------------------------
--------//
// An enhanced relative of scoped_ptr with reference counted copy
semantics.
// The object pointed to is deleted when the last shared_ptr pointing
to it
// is destroyed or reset.
template< typename T , typename Deleter = deleter<T> > class
shared_ptr : public base_ptr< T , Deleter >
{
public:
typedef T element_type;
explicit shared_ptr(T* p =0,const Deleter& d = deleter<T>()) :
base_ptr<T,Deleter>(p,d)
{
try
{
pn = new long(1);
} // fix: prevent leak if new throws
catch (...)
{
del();
throw;
}
}
~shared_ptr() { dispose(); }
shared_ptr(const shared_ptr& r) throw() : base_ptr<T,Deleter>(r)
{
++*(pn = r.pn);
}
shared_ptr(std::auto_ptr<T>& r,const Deleter& d = deleter<T>()) :
base_ptr<T,Deleter>(NULL,d)
{
pn = new long(1); // may throw
ptr() = r.release(); // fix: moved here to stop leak if new throws
}
shared_ptr& operator=(const shared_ptr& r)
{
share(r.get(),r.pn);
return *this;
}
shared_ptr& operator=(std::auto_ptr<T>& r)
{
// code choice driven by guarantee of "no effect if new throws"
if (*pn == 1)
{
deleter(px);
}
else
{
// allocate new reference counter
long * tmp = new long(1); // may throw
--*pn; // only decrement once danger of new throwing is past
pn = tmp;
} // allocate new reference counter
ptr() = r.release(); // fix: moved here so doesn't leak if new
throws
return *this;
}
void reset(T* p=0)
{
if ( ptr() == p )
return; // fix: self-assignment safe
if (--*pn == 0)
{
del();
}
else
{ // allocate new reference counter
try
{
pn = new long;
} // fix: prevent leak if new throws
catch (...)
{
++*pn; // undo effect of --*pn above to meet effects guarantee
del(p);
throw;
} // catch
} // allocate new reference counter
*pn = 1;
ptr() = p;
} // reset
long use_count() const throw(){ return *pn; }
bool unique() const throw() { return *pn == 1; }
T* release() throw()
{
*pn = 0;
T* ret = get();
ptr() = NULL;
return ret;
}
void swap(shared_ptr<T>& other) throw()
{
std::swap(ptr(),other.ptr());
std::swap(pn,other.pn);
}
protected:
long* pn; // Counter
void dispose() { if (--*pn == 0) { del(); delete pn; } }
void share(T* rpx, long* rpn)
{
if (pn != rpn)
{
dispose();
ptr() = rpx;
++*(pn = rpn);
}
} // share
}; // shared_ptr
template <class T,class Deleter = array_deleter<T> > class
shared_array : public shared_ptr<T,Deleter>
{
public:
explicit shared_array(T* p =0,const Deleter& d = array_deleter<T>())
: shared_ptr<T,Deleter>(p,d) {};
~shared_array() { dispose(); }
T& operator[](size_t i) const throw() { return ptr()[i]; }
};
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk