How is the std::tr1::shared_ptr implemented?
shared_ptr
must manage a reference counter and the carrying of a deleter functor that is deduced by the type of the object given at initialization.
The shared_ptr
class typically hosts two members: a T*
(that is returned by operator->
and dereferenced in operator*
) and a aux*
where aux
is a inner abstract class that contains:
- a counter (incremented / decremented upon copy-assign / destroy)
- whatever is needed to make increment / decrement atomic (not needed if specific platform atomic INC/DEC is available)
- an abstract
virtual destroy()=0;
- a virtual destructor.
Such aux
class (the actual name depends on the implementation) is derived by a family of templatized classes (parametrized on the type given by the explicit constructor, say U
derived from T
), that add:
- a pointer to the object (same as
T*
, but with the actual type: this is needed to properly manage all the cases ofT
being a base for whateverU
having multipleT
in the derivation hierarchy) - a copy of the
deletor
object given as deletion policy to the explicit constructor (or the defaultdeletor
just doing deletep
, wherep
is theU*
above) - the override of the destroy method, calling the deleter functor.
A simplified sketch can be this one:
template<class T>
class shared_ptr
{
struct aux
{
unsigned count;
aux() :count(1) {}
virtual void destroy()=0;
virtual ~aux() {} //must be polymorphic
};
template<class U, class Deleter>
struct auximpl: public aux
{
U* p;
Deleter d;
auximpl(U* pu, Deleter x) :p(pu), d(x) {}
virtual void destroy() { d(p); }
};
template<class U>
struct default_deleter
{
void operator()(U* p) const { delete p; }
};
aux* pa;
T* pt;
void inc() { if(pa) interlocked_inc(pa->count); }
void dec()
{
if(pa && !interlocked_dec(pa->count))
{ pa->destroy(); delete pa; }
}
public:
shared_ptr() :pa(), pt() {}
template<class U, class Deleter>
shared_ptr(U* pu, Deleter d) :pa(new auximpl<U,Deleter>(pu,d)), pt(pu) {}
template<class U>
explicit shared_ptr(U* pu) :pa(new auximpl<U,default_deleter<U> >(pu,default_deleter<U>())), pt(pu) {}
shared_ptr(const shared_ptr& s) :pa(s.pa), pt(s.pt) { inc(); }
template<class U>
shared_ptr(const shared_ptr<U>& s) :pa(s.pa), pt(s.pt) { inc(); }
~shared_ptr() { dec(); }
shared_ptr& operator=(const shared_ptr& s)
{
if(this!=&s)
{
dec();
pa = s.pa; pt=s.pt;
inc();
}
return *this;
}
T* operator->() const { return pt; }
T& operator*() const { return *pt; }
};
Where weak_ptr
interoperability is required a second counter (weak_count
) is required in aux
(will be incremented / decremented by weak_ptr
), and delete pa
must happen only when both the counters reach zero.
shared_ptr in std::tr1
In G++ 4.3,
#include <tr1/memory>
should do the trick. You'll find shared_ptr
at std::tr1::shared_ptr
.
using std::tr1::shared_ptr as an internal mechanism for reference counting
Consider instead using a shared_ptr<FILE>
with a custom deleter:
struct fclose_deleter
{
void operator()(FILE* f)
{
if (f)
{
std::fclose(f);
}
}
};
Then, your File
class is much simpler (and correcter):
class File
{
public:
File(const char* path, const char* mode)
: _file(std::fopen(path, mode), fclose_deleter())
{
}
int write(void const* buff, size_t size)
{
// You'll want to verify that _file.get() is valid, or you'll want to
// throw in the constructor if the call to 'std::fopen()' fails.
std::fwrite(buff, size, 1, _file.get());
}
private:
std::tr1::shared_ptr<FILE> _file;
};
Casting std::tr1::shared_ptrT and std::shared_ptrT with same function but different overloads
Make your Cast
function template take a template template parameter:
template<typename T, template<class> class SP, class U>
SP<T> Cast2(SP<U> const& sp) {
using std::dynamic_pointer_cast;
using std::tr1::dynamic_pointer_cast;
return dynamic_pointer_cast<T>(sp);
}
demo
Leaving the original answer for posterity. It is ill-formed on VC++ (though it works as expected), because there is no valid specialization of the function.
Disable the second overload if std::shared_ptr
and std::tr1::shared_ptr
are the same thing (they are on VC++ 10, they are not for my gcc).
template<class T, class U>
typename std::enable_if<
!std::is_same< std::shared_ptr<T>, std::tr1::shared_ptr<T> >::value,
std::tr1::shared_ptr<T>
>::type
Cast( const std::tr1::shared_ptr<U>& spObject ) // rename from CastTerrainObject
{
return std::tr1::dynamic_pointer_cast<T>(spObject);
}
The following compiles on both VC++ 10 and the latest gcc. Unfortunately, it's ill-formed on VC++10 (despite working as expected)
#include <memory>
#include <type_traits>
#ifndef _WIN32
#include <tr1/type_traits>
#include <tr1/shared_ptr.h>
#endif
template<class T, class U> // rename from CastTerrainObject
std::shared_ptr<T> Cast( const std::shared_ptr<U>& spObject )
{
return std::dynamic_pointer_cast<T>(spObject);
}
template<class T, class U>
typename std::enable_if<
!std::is_same< std::shared_ptr<T>, std::tr1::shared_ptr<T> >::value,
std::tr1::shared_ptr<T>
>::type
Cast( const std::tr1::shared_ptr<U>& spObject ) // rename from CastTerrainObject
{
return std::tr1::dynamic_pointer_cast<T>(spObject);
}
struct B{ virtual ~B(){} };
struct D:B{};
int main()
{
Cast<B>(std::make_shared<D>());
}
demo
You could also ifdef the second overload away, but I'm not sure which conditions should be checked.
Implementing std::equal with tr1::shared_ptr types
Easiest may be to use find_if
:
template <typename T>
struct shared_ptr_finder
{
T const & t;
shared_ptr_finder(T const & t_) : t(t_) { }
bool operator()(std::tr1::shared_ptr<T> const & p)
{
return *p == t;
}
};
template <typename T>
shared_ptr_finder<T> find_shared(std::tr1::shared_ptr<T> const & p)
{
return shared_ptr_finder<T>(*p);
}
#include <algorithm>
typedef std::vector< std::tr1::shared_ptr<Color> >::iterator it_type;
it_type it1 = std::find_if(myVector.begin(), myVector.end(), find_shared(p2));
it_type it2 = std::find_if(myVector.begin(), myVector.end(), shared_ptr_finder<Color>(*p2));
Is it legal to place using tr1::shared_ptr in namespace std in header?
Technically, the Standard says that you enter the realm of Undefined Behavior if you do this:
17.6.4.2.1 Namespace std [namespace.std]
1 The behavior of a C++ program is undefined if it adds declarations or
definitions to namespace std or to a namespace within namespace std
unless otherwise specified.
But in practice, you are likely to get away with it. Heck, even Scott Meyers proposed a similarly undefined namespace alias trick in Effective C++ 3rd Ed. (Item 54, p.268) to use Boost functionality as a stopgap for missing tr1
functionality.
namespace std { using namespace tr1 = ::boost; }
Your using declaration is also undefined behavior, but go ahead and jump right in.
NOTE: comment it with a big fat warning, #define
and #pragma
around your compiler version and warnings, and as soon as you upgrade to a compiler/library that actually has std::shared_ptr
, make sure to revisit that header and remove the code.
Any hit for dereferencing std::tr1:shared_ptr vs. dereferencing a naked pointer?
In an optimized build without debugging support, there shouldn't be any overhead. You can find out by taking a look at the implementation you are using. Chances are, its operator->
overload just returns the pointer to the pointed-to object and its operator*
overload just dereferences this pointer.
(This is what the Visual C++ 2010 implementation of std::shared_ptr
does: each of those overloaded operators just calls a "get" function which just returns the pointer; there is no locking or other overhead of any kind. Other implementations may be different.)
An unoptimized build may not inline the operator overload and if your implementation has extra debugging support that you enable, it may perform extra checks (e.g., perhaps an assert if you dereference a null pointer).
A std::tr1::shared_ptr for Objective C++ on iPhone?
As long as you learn the memory management rules first, there is no real problem with shared_ptr
- it can help you in C++ contexts but doesn't let the ownership questions magically disappear.shared_ptr
supports a custom deallocator so the following:
@interface A : NSObject
- (void)f;
@end
@implementation A
- (void)dealloc { NSLog(@"bye"); [super dealloc]; }
- (void)f { NSLog(@"moo"); }
@end
void my_dealloc(id p) {
[p release];
}
// ...
{
shared_ptr<A> p([[A alloc] init], my_dealloc);
[p.get() f];
}
... outputs:
moo
bye
... as expected.
If you want you can hide the deallocator from the user using a helper function, e.g.:
template<class T> shared_ptr<T> make_objc_ptr(T* t) {
return shared_ptr<T>(t, my_dealloc);
}
shared_ptr<A> p = make_objc_ptr<A>([[A alloc] init]);
Related Topics
Boost Zip_Iterator and Std::Sort
Calling a Virtual Function from the Constructor
Recommended Values for Opencv Detectmultiscale() Parameters
What Is a Constant Reference? (Not a Reference to a Constant)
Get Absolute Value Without Using Abs Function Nor If Statement
C++11 Way to Index Tuple at Runtime Without Using Switch
How to Use Valgrind with Python C++ Extensions
Understanding the Dangers of Sprintf(...)
In C++ What Causes an Assignment to Evaluate as True or False When Used in a Control Structure
Undefined Reference to 'Pthread_Key_Create' (Linker Error)
The New Keyword "Auto"; When Should It Be Used to Declare a Variable Type
New to Xcode Can't Open Files in C++
Cannot Get Opencv to Compile Because of Undefined References