Re: Should the shared_ptr have release method?
Thiago Adams wrote:
In the boost site there is an FAQ answering why the shared_ptr
doesn't have the release method.
http://www.boost.org/libs/smart_ptr/shared_ptr.htm#FAQ
In general I think that the shared_ptr is not a tool just to
"share" pointers, but it is very useful to simplify the
implementations of exceptions safe functions.
The Motivation for "release()" method.
I want to return a vector of pointers of the type "Item". The
object "Item" can throw exceptions in constructor and I am trying to
create an exception safe function.
What is an "exception safe" function exactly?
There are many ways to implement it, but actually I didn't find an
elegant way to do this.
The simple way is using two vectors of shared_ptr.
void (vector< shared_ptr<Item> > &vec)
{
vector< shared_ptr <Item> > local;
for (int i = 0; i < N, i++)
{
local.push_back(shared_ptr<Item> (new Item(i) ) );
}
local.swap(vec);
}
Why not insert the items into the vector directly? What does the local
vector contribute to this routine? I suspect that you are interested in
ensuring the atomicity or "idempotency" of the operation of filling the
vector. In other words, when adding a particular set of items to a
vector, either all of the items are to be added or no items are to be
added. But in that case why not simply clear the vector when the
exception thown when one of the items cannot be added - is caught:
void (vector< shared_ptr<Item> >& vec)
{
try {
for (int i = 0; i < N, i++)
vec.push_back( shared_ptr<Item>(new Item(i)) );
}
catch (...)
{
vec.clear();
throw;
}
}
However, I think I should not penalize the caller to use a vector of
shared_ptrs, because the caller doesn't share pointers with anyone
else, and the simple RAII is enough. In top of that, sometimes the
caller needs to use Item * instead a vector<Item*> because it was
transferring data using C api. (For instance transferring buffers using
&vec[0])
So, what I need is to implement a function to swap between vector<
shared_ptr<Item> and vector< Item * >.
Why swap? Why not copy the pointers frorm the shared_ptr vector into
the pointer vector:
#include <algorithm>
#include <vector>
#include <functional>
using std::tr1::shared_ptr;
using std::vector;
using std::mem_fun_ref;
typedef shared_ptr<Item> ItemPtr;
...
vector< ItemPtr> v;
vector< Item*> v2;
...
v2.resize( v.size());
std::transform( v.begin(), v.end(), v2.begin(),
mem_fun_ref(&ItemPtr::get));
To create this function I need to remove ownership of shared_ptrs and
transfer to vector< Item * >. It is impossible because the shared_ptr
doesn't have release.
The questions are:
Should the shared_ptr have release method that works only if
use_count() == 1, and throws if use_count() > 1 ?
Am I using the wrong approach? There is a different smart pointer for
this?
Should we create a custom container to deal with this kind o problem?
I don't see how a custom container would implement the behavior you
want. A custom class to wrap the shared_ptr should work:
#include <algorithm>
#include <vector>
#include <functional>
#include <stdexcept>
using std::vector;
using std::mem_fun_ref;
using std::tr1::shared_ptr;
struct Item
{
Item(int i) {}
~Item() { std::cout << "deleting Item\n" ; }
};
struct released_deleter
{
public:
released_deleter() : released_(false) {}
void release() { released_ = true; }
template <class T>
void operator()( const T* t)
{
if (not released_)
delete t;
else
std::cout << "not deleting Item\n";
}
private:
bool released_;
};
class ItemPtr
{
public:
explicit ItemPtr(int i)
: ptr_(new Item(i), released_deleter())
{
}
Item* operator->() { return ptr_.get(); }
Item* get() { return ptr_.get(); }
void release()
{
if (not ptr_.unique())
throw std::runtime_error("released ptr not unique");
std::tr1::get_deleter<released_deleter>( ptr_)->release();
}
private:
shared_ptr<Item> ptr_;
};
int main()
{
vector< ItemPtr> v;
v.push_back( ItemPtr(1) );
v.push_back( ItemPtr(2) );
v.push_back( ItemPtr(3) );
v.push_back( ItemPtr(4) );
v.clear();
v.push_back( ItemPtr(1) );
v.push_back( ItemPtr(2) );
v.push_back( ItemPtr(3) );
v.push_back( ItemPtr(4) );
std::for_each( v.begin(), v.end(),
mem_fun_ref(&ItemPtr::release));
v.clear();
}
Program Output:
deleting Item
deleting Item
deleting Item
deleting Item
not deleting Item
not deleting Item
not deleting Item
not deleting Item
Greg
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]