-
Notifications
You must be signed in to change notification settings - Fork 38
/
shared_ptr.hpp
294 lines (271 loc) · 8.49 KB
/
shared_ptr.hpp
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/**
* @file shared_ptr.hpp
* @brief shared_ptr is a minimal implementation of smart pointer, a subset of the C++11 std::shared_ptr or boost::shared_ptr.
*
* Copyright (c) 2013-2019 Sebastien Rombauts ([email protected])
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#pragma once
#include <cstddef> // NULL
#include <algorithm> // std::swap
// can be replaced by other error mechanism
#include <cassert>
#define SHARED_ASSERT(x) assert(x)
/**
* @brief implementation of reference counter for the following minimal smart pointer.
*
* shared_ptr_count is a container for the allocated pn reference counter.
*/
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
};
class shared_ptr_base
{
protected:
shared_ptr_base(void) :
pn()
{
}
shared_ptr_base(const shared_ptr_base& other) :
pn(other.pn)
{
}
shared_ptr_count pn; //!< Reference counter
};
/**
* @brief minimal implementation of smart pointer, a subset of the C++11 std::shared_ptr or boost::shared_ptr.
*
* shared_ptr is a smart pointer retaining ownership of an object through a provided pointer,
* and sharing this ownership with a reference counter.
* It destroys the object when the last shared pointer pointing to it is destroyed or reset.
*/
template<class T>
class shared_ptr: public shared_ptr_base
{
public:
/// The type of the managed object, aliased as member type
typedef T element_type;
/// @brief Default constructor
shared_ptr(void) throw() : // never throws
shared_ptr_base(),
px(NULL)
{
}
/// @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
shared_ptr_base()
{
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
shared_ptr_base(ptr)
{
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),
shared_ptr_base(ptr)
{
SHARED_ASSERT((NULL == ptr.get()) || (0 != ptr.use_count())); // must be coherent : no allocation allowed in this path
acquire(static_cast<typename shared_ptr<T>::element_type*>(ptr.get())); // 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),
shared_ptr_base(ptr)
{
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
~shared_ptr(void) throw() // never throws
{
release();
}
/// @brief this reset releases its ownership
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 :
operator bool() const throw() // never throws
{
return (0 < pn.use_count());
}
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 :
T& operator*() const throw() // never throws
{
SHARED_ASSERT(NULL != px);
return *px;
}
T* operator->() const throw() // never throws
{
SHARED_ASSERT(NULL != px);
return px;
}
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
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
void release(void) throw() // never throws
{
pn.release(px);
px = NULL;
}
private:
T* px; //!< Native pointer
};
// comparaison operators
template<class T, class U> bool operator==(const shared_ptr<T>& l, const shared_ptr<U>& r) throw() // never throws
{
return (l.get() == r.get());
}
template<class T, class U> bool operator!=(const shared_ptr<T>& l, const shared_ptr<U>& r) throw() // never throws
{
return (l.get() != r.get());
}
template<class T, class U> bool operator<=(const shared_ptr<T>& l, const shared_ptr<U>& r) throw() // never throws
{
return (l.get() <= r.get());
}
template<class T, class U> bool operator<(const shared_ptr<T>& l, const shared_ptr<U>& r) throw() // never throws
{
return (l.get() < r.get());
}
template<class T, class U> bool operator>=(const shared_ptr<T>& l, const shared_ptr<U>& r) throw() // never throws
{
return (l.get() >= r.get());
}
template<class T, class U> bool operator>(const shared_ptr<T>& l, const shared_ptr<U>& r) throw() // never throws
{
return (l.get() > r.get());
}
// static cast of shared_ptr
template<class T, class U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& ptr) // never throws
{
return shared_ptr<T>(ptr, static_cast<typename shared_ptr<T>::element_type*>(ptr.get()));
}
// dynamic cast of shared_ptr
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& ptr) // never throws
{
T* p = dynamic_cast<typename shared_ptr<T>::element_type*>(ptr.get());
if (NULL != p)
{
return shared_ptr<T>(ptr, p);
}
else
{
return shared_ptr<T>();
}
}