C++ Library Extensions 2022.12.09
To help learn modern C++ programming
022-iterators.cpp
Go to the documentation of this file.
1#include <tpf_output.hpp>
2#include <cstdlib>
3#include <iterator>
4
7
8template<typename ElementType>
10{
11public:
12 using iterator = ElementType*;
13
14 // don't do this
15 // using const_iterator = const iterator;
16 // it will define ElementType* const, it is wrong
17
18 using const_iterator = const ElementType*;
19
20private:
21 size_t m_size;
22 ElementType *m_ptr;
23
24 void free_memory()
25 {
26 if (this->m_size != 0 || m_ptr != nullptr)
27 {
28 delete[] m_ptr;
29 this->m_ptr = nullptr;
30 this->m_size = 0;
31 }
32 }
33
34 void invalidate()
35 {
36 this->m_ptr = nullptr;
37 this->m_size = 0;
38 }
39
40public:
41
42 // the pointer pointing to the first element
44 {
45 // return this->m_ptr;
46 return &this->m_ptr[0];
47 }
48
49 // the pointer pointing to the element after the last element
51 {
52 // return (this->m_ptr + this->m_size);
53 return &this->m_ptr[this->m_size];
54 }
55
57 {
58 // we actually do NOT need the cast,
59 // but I want to make the syntax clear
60 return static_cast<const_iterator>(&this->m_ptr[0]);
61 }
62
64 {
65 // we actually do NOT need the cast,
66 // but I want to make the syntax clear
67 return static_cast<const_iterator>(&this->m_ptr[this->m_size]);
68 }
69
70 auto rbegin()
71 {
72 return std::reverse_iterator(end());
73 }
74
75 auto rend()
76 {
77 return std::reverse_iterator(begin());
78 }
79
80 auto crbegin()
81 {
82 return std::reverse_iterator(cend());
83 }
84
85 auto crend()
86 {
87 return std::reverse_iterator(cbegin());
88 }
89
90 size_t size() const { return this->m_size; }
91
92 void resize(size_t count)
93 {
94 if(count < 1)
95 Tpf_ThrowDebugException("count should be greater than 0");
96 else if(count == this->m_size)
97 return;
98 else
99 {
100 // free existing memory
101 this->free_memory();
102
103 try
104 {
105 this->m_size = count;
106 this->m_ptr = new ElementType[this->m_size];
107 }
108 catch(...)
109 {
110 Tpf_ThrowDebugException("Dynamic Allocation Failed");
111 }
112 }
113 }
114
115 // this is dangerous practice
116 // if dynamic allocation fails,
117 // this constructor throws std::bad_alloc exception
118 explicit dynamic_array(size_t size = 1)
119 : m_size{size}
120 {
121 stream << "Default constructor called" << endl;
122
123 try
124 {
125 this->m_ptr = size > 0 ? new ElementType[size] : nullptr;
126 }
127 catch(...)
128 {
129 // it reports details about allocation failture
130 Tpf_ThrowDebugException("Dynamic Allocation Failed");
131 }
132 }
133
134 // if dynamic allocation fails,
135 // it throws exception
136 dynamic_array(const dynamic_array &right_hand_side) :
137 m_size{right_hand_side.m_size}
138 {
139 stream << "Copy constructor called" << endl;
140
141 try
142 {
143 this->m_ptr = this->m_size > 0 ? new ElementType[this->m_size] : nullptr;
144
145 if (m_size > 0)
146 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
147 }
148 catch(...)
149 {
150 // it reports details about allocation failture
151 Tpf_ThrowDebugException("Dynamic Allocation Failed");
152 }
153
154 }
155
156 ElementType *operator&() { return this->m_ptr; }
157
158 ElementType &operator[](size_t index) { return this->m_ptr[index]; }
159
160 const ElementType &operator[](size_t index) const { return this->m_ptr[index]; }
161
162 ElementType &at(size_t index)
163 {
164 if (index < this->m_size && this->m_ptr != nullptr)
165 return this->m_ptr[index];
166 else
167 Tpf_ThrowDebugException("Index Out of Range");
168 }
169
170 const ElementType &at(size_t index) const
171 {
172 if (index < this->m_size && this->m_ptr != nullptr)
173 return this->m_ptr[index];
174 else
175 Tpf_ThrowDebugException("Index Out of Range");
176 }
177
178 // if allocation fails,
179 // it can throw std::bad_alloc exception
180 dynamic_array &operator=(const dynamic_array &right_hand_side)
181 {
182 stream << "Copy assignment operator() called" << endl;
183
184 // this code is wrong, because address of operator&()
185 // is defined for class dynamic_array,
186 // so, we cannot use the following code any longer
187 // if(this != &right_hand_side)
188
189 if (this != std::addressof(right_hand_side))
190 {
191 if (this->m_size == right_hand_side.m_size)
192 {
193 // we do not need to reallocate memory
194 // and also, we do not need to free memory
195 // this->free_memory()
196
197 if (m_size > 0)
198 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
199 }
200 else
201 {
202 // we have free existing memory
203 this->free_memory();
204 this->m_size = right_hand_side.m_size;
205
206 if (this->m_size > 0)
207 {
208 try
209 {
210 // and we have to reallocate new memory
211 this->m_ptr = new ElementType[this->m_size];
212 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
213 }
214 catch(...)
215 {
216 // it reports details about allocation failture
217 Tpf_ThrowDebugException("Dynamic Allocation Failed");
218 }
219 }
220 }
221 }
222
223 return *this;
224 }
225
226 dynamic_array(dynamic_array &&right_hand_side) noexcept
227 : m_size{right_hand_side.m_size},
228 m_ptr{right_hand_side.m_ptr}
229 {
230 stream << "Move constructor called" << endl;
231
232 // IMPORTANT: invalidate right_hand_side
233 // after move, right_hand_side is invalide
234 // so, we should NOT access to right_hand_side after move operation
235 right_hand_side.invalidate();
236 }
237
238 dynamic_array &operator=(dynamic_array &&right_hand_side) noexcept
239 {
240 stream << "Move assignment operator() called" << endl;
241
242 if (this != std::addressof(right_hand_side))
243 {
244 // delete existing memory
245 this->free_memory();
246 this->m_size = right_hand_side.m_size;
247 this->m_ptr = right_hand_side.m_ptr;
248
249 // IMPORTANT: invalidate right_hand_size after move assignment
250 // since after move operation, right_hand_side is invalid
251 // so, we should NOT access to right_hand_side after move operation
252 right_hand_side.invalidate();
253 }
254
255 return *this;
256 }
257
259 {
260 stream << "Destructor called" << endl;
261
262 this->free_memory();
263 }
264
265 friend std::ostream& operator<<(std::ostream& os, const dynamic_array& da)
266 {
267 if(da.size() < 1)
268 os << "{ }";
269 else
270 {
271 size_t size = da.size() - 1;
272 os << "{ ";
273
274 for(size_t i = 0; i < size; ++i)
275 os << da[i] << ", ";
276
277 os << da[size] << " }";
278 }
279
280 return os;
281 }
282};
283
285{
286 dynamic_array<int> a{10};
287
288 for(size_t i = 0; i < a.size(); ++i)
289 a[i] = (int)i;
290
291 stream << "a = " << a << endl;
292}
293
295{
296 size_t count = 2;
297
298 std::vector< dynamic_array<int> > jagged_array(count); // don't do this
299
300 stream << "At this point, default constructor of dynamic_array is called "
301 << count << " times.\n"<< endl;
302
303 for(size_t i = 0; i < jagged_array.size(); ++i)
304 jagged_array[i] = dynamic_array<int>{ i + 1 };
305
306 stream << "In the for loop, default constructor of dynamic_array is called "
307 << count << " times, and move constructor is called " << count << " times\n" << endl;
308
309 stream << "So, destructor is called total: " << (count + count) << " times " << endl;
310}
311
313{
314 stream << "\nthis is better, but not perfect!!\n" << endl;
315
316 size_t count = 2;
317
318 std::vector< dynamic_array<int> > jagged_array;
319 jagged_array.reserve(count);
320
321 for(size_t i = 0; i < jagged_array.capacity(); ++i)
322 jagged_array.emplace_back(dynamic_array<int>{ i + 1 });
323}
324
325// we never created temporary dynamic array in this function
327{
328 stream << "\nthis is PEREFECT WAY, YOU SHOULD ALWAYS USE THIS METHOD!!\n" << endl;
329
330 size_t count = 10;
331
332 std::vector< dynamic_array<int> > jagged_array;
333 jagged_array.reserve(count);
334
335 for(size_t i = 0; i < jagged_array.capacity(); ++i)
336 {
337 jagged_array.emplace_back(i + 1);
338 // i + 1 is argument for dynamic_array.
339
340 // now std::vector creates dynamic_array
341 // internally, so, no move... only count times of default
342 // constructor of dynamic_array is called.
343
344 for(size_t j = 0; j < jagged_array.back().size(); ++j)
345 jagged_array.back()[j] = (int)j;
346 }
347
348 // note that the constructor of dynamic_array{size_t}
349
350 for(auto& da: jagged_array)
351 {
352 stream << da << endl;
353 }
354}
355
357{
358 // we dynamically allocated 10 elements
359 dynamic_array<int> array{ 10 };
360
361 int i = 0;
362
363 for(auto& e: array)
364 e = i++;
365
366 stream << array << endl;
367
368 tpf::sstream out;
369
370 std::for_each(array.rbegin(), array.rend(),
371 [&out](auto& e)
372 {
373 out << e << ", ";
374 });
375
376 // if you don't endl with stream, you cannot see the output
377 out << endl;
378}
379
381{
382 dynamic_array<int> array{10};
383
384 int i = -1;
385
386 for(auto& e: array)
387 e = ++i;
388
389 stream << array << endl;
390
391 // what does it mean?
392 // array = 20;
393 // stream << array << endl;
394}
395
396int main()
397{
398 // examples_for_dynamic_array();
399 // do_not_do_this();
400
401 // is_still_is_not_perfect();
402
403 // please_use_this_method();
404
405 // test_iterators_dynamic_array();
406
408}
void test_iterators_dynamic_array()
void examples_for_dynamic_array()
void why_explicit_to_constructor()
tpf::sstream stream
void do_not_do_this()
auto endl
void please_use_this_method()
void is_still_is_not_perfect()
int main()
std::atomic< int > count
Definition: 022-mutex.cpp:10
ElementType * operator&()
dynamic_array(dynamic_array &&right_hand_side) noexcept
const_iterator cbegin()
ElementType & operator[](size_t index)
const ElementType * const_iterator
iterator end()
iterator begin()
dynamic_array(const dynamic_array &right_hand_side)
const ElementType & at(size_t index) const
void resize(size_t count)
const_iterator cend()
dynamic_array & operator=(dynamic_array &&right_hand_side) noexcept
ElementType & at(size_t index)
const ElementType & operator[](size_t index) const
friend std::ostream & operator<<(std::ostream &os, const dynamic_array &da)
dynamic_array(size_t size=1)
size_t size() const
ElementType * iterator
dynamic_array & operator=(const dynamic_array &right_hand_side)
constexpr auto endl
Definition: tpf_output.hpp:973
Stream output operators << are implemented.
#define Tpf_ThrowDebugException(debug_message)
Throw a debug_exception with message as argument.
Definition: tpf_types.hpp:1416