Minimal shared_ptr Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
class shared_ptr_count
{
public:
shared_ptr_count() :
pn(NULL)
{
}
shared_ptr_count(const shared_ptr_count& count) :
pn(count.pn)
{
}
/// @brief Swap method for the copy-and-swap idiom (copy constructor and swap method)
void swap(shared_ptr_count& lhs) throw() // never throws
{
std::swap(pn, lhs.pn);
}
/// @brief getter of the underlying reference counter
long use_count(void) const throw() // never throws
{
long count = 0;
if (NULL != pn)
{
count = *pn;
}
return count;
}
/// @brief acquire/share the ownership of the pointer, initializing the reference counter
template<class U>
void acquire(U* p) // may throw std::bad_alloc
{
if (NULL != p)
{
if (NULL == pn)
{
try
{
pn = new long(1); // may throw std::bad_alloc
}
catch (std::bad_alloc&)
{
delete p;
throw; // rethrow the std::bad_alloc
}
}
else
{
++(*pn);
}
}
}
/// @brief release the ownership of the px pointer, destroying the object when appropriate
template<class U>
void release(U* p) throw() // never throws
{
if (NULL != pn)
{
--(*pn);
if (0 == *pn)
{
delete p;
delete pn;
}
pn = NULL;
}
}

public:
long* pn; //!< Reference counter
};

template<class T>
class shared_ptr
{
public:
/// The type of the managed object, aliased as member type
typedef T element_type;

/// @brief Default constructor
shared_ptr(void) throw() : // never throws
px(NULL),
pn()
{
}
/// @brief Constructor with the provided pointer to manage
explicit shared_ptr(T* p) : // may throw std::bad_alloc
//px(p), would be unsafe as acquire() may throw, which would call release() in destructor
pn()
{
acquire(p); // may throw std::bad_alloc
}
/// @brief Constructor to share ownership. Warning : to be used for pointer_cast only ! (does not manage two separate <T> and <U> pointers)
template <class U>
shared_ptr(const shared_ptr<U>& ptr, T* p) :
//px(p), would be unsafe as acquire() may throw, which would call release() in destructor
pn(ptr.pn)
{
acquire(p); // may throw std::bad_alloc
}
/// @brief Copy constructor to convert from another pointer type
template <class U>
shared_ptr(const shared_ptr<U>& ptr) throw() : // never throws (see comment below)
//px(ptr.px),
pn(ptr.pn)
{
SHARED_ASSERT((NULL == ptr.px) || (0 != ptr.pn.use_count())); // must be coherent : no allocation allowed in this path
acquire(static_cast<typename shared_ptr<T>::element_type*>(ptr.px)); // will never throw std::bad_alloc
}
/// @brief Copy constructor (used by the copy-and-swap idiom)
shared_ptr(const shared_ptr& ptr) throw() : // never throws (see comment below)
//px(ptr.px),
pn(ptr.pn)
{
SHARED_ASSERT((NULL == ptr.px) || (0 != ptr.pn.use_count())); // must be cohérent : no allocation allowed in this path
acquire(ptr.px); // will never throw std::bad_alloc
}
/// @brief Assignment operator using the copy-and-swap idiom (copy constructor and swap method)
shared_ptr& operator=(shared_ptr ptr) throw() // never throws
{
swap(ptr);
return *this;
}
/// @brief the destructor releases its ownership
inline ~shared_ptr(void) throw() // never throws
{
release();
}
/// @brief this reset releases its ownership
inline void reset(void) throw() // never throws
{
release();
}
/// @brief this reset release its ownership and re-acquire another one
void reset(T* p) // may throw std::bad_alloc
{
SHARED_ASSERT((NULL == p) || (px != p)); // auto-reset not allowed
release();
acquire(p); // may throw std::bad_alloc
}

/// @brief Swap method for the copy-and-swap idiom (copy constructor and swap method)
void swap(shared_ptr& lhs) throw() // never throws
{
std::swap(px, lhs.px);
pn.swap(lhs.pn);
}

// reference counter operations :
inline operator bool() const throw() // never throws
{
return (0 < pn.use_count());
}
inline bool unique(void) const throw() // never throws
{
return (1 == pn.use_count());
}
long use_count(void) const throw() // never throws
{
return pn.use_count();
}

// underlying pointer operations :
inline T& operator*() const throw() // never throws
{
SHARED_ASSERT(NULL != px);
return *px;
}
inline T* operator->() const throw() // never throws
{
SHARED_ASSERT(NULL != px);
return px;
}
inline T* get(void) const throw() // never throws
{
// no assert, can return NULL
return px;
}

private:
/// @brief acquire/share the ownership of the px pointer, initializing the reference counter
inline void acquire(T* p) // may throw std::bad_alloc
{
pn.acquire(p); // may throw std::bad_alloc
px = p; // here it is safe to acquire the ownership of the provided raw pointer, where exception cannot be thrown any more
}

/// @brief release the ownership of the px pointer, destroying the object when appropriate
inline void release(void) throw() // never throws
{
pn.release(px);
px = NULL;
}

private:
// This allow pointer_cast functions to share the reference counter between different shared_ptr types
template<class U>
friend class shared_ptr;

private:
T* px; //!< Native pointer
shared_ptr_count pn; //!< Reference counter
};