C++ Library Extensions 2022.12.09
To help learn modern C++ programming
023-invariant.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 // we assume count is greater than zero
93 void resize(size_t count)
94 {
95 if(count == this->m_size)
96 return;
97 else
98 {
99 // free existing memory
100 this->free_memory();
101
102 try
103 {
104 this->m_size = count;
105 this->m_ptr = new ElementType[this->m_size];
106 }
107 catch(...)
108 {
109 Tpf_ThrowDebugException("Dynamic Allocation Failed");
110 }
111 }
112 }
113
114 // this is dangerous practice
115 // if dynamic allocation fails,
116 // this constructor throws std::bad_alloc exception
117 explicit dynamic_array(size_t size = 1)
118 : m_size{size}
119 {
120 stream << "Default constructor called" << endl;
121
122 try
123 {
124 this->m_ptr = new ElementType[size];
125 }
126 catch(...)
127 {
128 // it reports details about allocation failture
129 Tpf_ThrowDebugException("Dynamic Allocation Failed");
130 }
131 }
132
133 // if dynamic allocation fails,
134 // it throws exception
135 dynamic_array(const dynamic_array &right_hand_side) :
136 m_size{right_hand_side.m_size}
137 {
138 stream << "Copy constructor called" << endl;
139
140 try
141 {
142 this->m_ptr = new ElementType[this->m_size];
143 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
144 }
145 catch(...)
146 {
147 // it reports details about allocation failture
148 Tpf_ThrowDebugException("Dynamic Allocation Failed");
149 }
150
151 }
152
153 ElementType *operator&() { return this->m_ptr; }
154
155 ElementType &operator[](size_t index) { return this->m_ptr[index]; }
156
157 const ElementType &operator[](size_t index) const { return this->m_ptr[index]; }
158
159 ElementType &at(size_t index)
160 {
161 if (index < this->m_size && this->m_ptr != nullptr)
162 return this->m_ptr[index];
163 else
164 Tpf_ThrowDebugException("Index Out of Range");
165 }
166
167 const ElementType &at(size_t index) const
168 {
169 if (index < this->m_size && this->m_ptr != nullptr)
170 return this->m_ptr[index];
171 else
172 Tpf_ThrowDebugException("Index Out of Range");
173 }
174
175 // if allocation fails,
176 // it can throw std::bad_alloc exception
177 dynamic_array &operator=(const dynamic_array &right_hand_side)
178 {
179 stream << "Copy assignment operator() called" << endl;
180
181 // this code is wrong, because address of operator&()
182 // is defined for class dynamic_array,
183 // so, we cannot use the following code any longer
184 // if(this != &right_hand_side)
185
186 if (this != std::addressof(right_hand_side))
187 {
188 if (this->m_size == right_hand_side.m_size)
189 {
190 // we do not need to reallocate memory
191 // and also, we do not need to free memory
192 // this->free_memory()
193
194 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
195 }
196 else
197 {
198 // we have free existing memory
199 this->free_memory();
200 this->m_size = right_hand_side.m_size;
201
202 try
203 {
204 // and we have to reallocate new memory
205 this->m_ptr = new ElementType[this->m_size];
206 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
207 }
208 catch(...)
209 {
210 // it reports details about allocation failture
211 Tpf_ThrowDebugException("Dynamic Allocation Failed");
212 }
213
214 }
215 }
216
217 return *this;
218 }
219
220 dynamic_array(dynamic_array &&right_hand_side) noexcept
221 : m_size{right_hand_side.m_size},
222 m_ptr{right_hand_side.m_ptr}
223 {
224 stream << "Move constructor called" << endl;
225
226 // IMPORTANT: invalidate right_hand_side
227 // after move, right_hand_side is invalide
228 // so, we should NOT access to right_hand_side after move operation
229 right_hand_side.invalidate();
230 }
231
232 dynamic_array &operator=(dynamic_array &&right_hand_side) noexcept
233 {
234 stream << "Move assignment operator() called" << endl;
235
236 if (this != std::addressof(right_hand_side))
237 {
238 // delete existing memory
239 this->free_memory();
240 this->m_size = right_hand_side.m_size;
241 this->m_ptr = right_hand_side.m_ptr;
242
243 // IMPORTANT: invalidate right_hand_size after move assignment
244 // since after move operation, right_hand_side is invalid
245 // so, we should NOT access to right_hand_side after move operation
246 right_hand_side.invalidate();
247 }
248
249 return *this;
250 }
251
253 {
254 stream << "Destructor called" << endl;
255
256 this->free_memory();
257 }
258
259 friend std::ostream& operator<<(std::ostream& os, const dynamic_array& da)
260 {
261 size_t size = da.size() - 1;
262 os << "{ ";
263
264 for(size_t i = 0; i < size; ++i)
265 os << da[i] << ", ";
266
267 os << da[size] << " }";
268
269 return os;
270 }
271};
272
274{
275 dynamic_array<int> a{10};
276
277 for(size_t i = 0; i < a.size(); ++i)
278 a[i] = (int)i;
279
280 stream << "a = " << a << endl;
281}
282
284{
285 size_t count = 2;
286
287 std::vector< dynamic_array<int> > jagged_array(count); // don't do this
288
289 stream << "At this point, default constructor of dynamic_array is called "
290 << count << " times.\n"<< endl;
291
292 for(size_t i = 0; i < jagged_array.size(); ++i)
293 jagged_array[i] = dynamic_array<int>{ i + 1 };
294
295 stream << "In the for loop, default constructor of dynamic_array is called "
296 << count << " times, and move constructor is called " << count << " times\n" << endl;
297
298 stream << "So, destructor is called total: " << (count + count) << " times " << endl;
299}
300
302{
303 stream << "\nthis is better, but not perfect!!\n" << endl;
304
305 size_t count = 2;
306
307 std::vector< dynamic_array<int> > jagged_array;
308 jagged_array.reserve(count);
309
310 for(size_t i = 0; i < jagged_array.capacity(); ++i)
311 jagged_array.emplace_back(dynamic_array<int>{ i + 1 });
312}
313
314// we never created temporary dynamic array in this function
316{
317 stream << "\nthis is PEREFECT WAY, YOU SHOULD ALWAYS USE THIS METHOD!!\n" << endl;
318
319 size_t count = 10;
320
321 std::vector< dynamic_array<int> > jagged_array;
322 jagged_array.reserve(count);
323
324 for(size_t i = 0; i < jagged_array.capacity(); ++i)
325 {
326 jagged_array.emplace_back(i + 1);
327 // i + 1 is argument for dynamic_array.
328
329 // now std::vector creates dynamic_array
330 // internally, so, no move... only count times of default
331 // constructor of dynamic_array is called.
332
333 for(size_t j = 0; j < jagged_array.back().size(); ++j)
334 jagged_array.back()[j] = (int)j;
335 }
336
337 // note that the constructor of dynamic_array{size_t}
338
339 for(auto& da: jagged_array)
340 {
341 stream << da << endl;
342 }
343}
344
346{
347 // we dynamically allocated 10 elements
348 dynamic_array<int> array{ 10 };
349
350 int i = 0;
351
352 for(auto& e: array)
353 e = i++;
354
355 stream << array << endl;
356
357 tpf::sstream out;
358
359 std::for_each(array.rbegin(), array.rend(),
360 [&out](auto& e)
361 {
362 out << e << ", ";
363 });
364
365 // if you don't endl with stream, you cannot see the output
366 out << endl;
367}
368
370{
371 dynamic_array<int> array{10};
372
373 int i = -1;
374
375 for(auto& e: array)
376 e = ++i;
377
378 stream << array << endl;
379
380 // what does it mean?
381 // array = 20;
382 // stream << array << endl;
383}
384
385int main()
386{
387 // examples_for_dynamic_array();
388 // do_not_do_this();
389
390 // is_still_is_not_perfect();
391
392 // please_use_this_method();
393
394 // test_iterators_dynamic_array();
395
397}
std::atomic< int > count
Definition: 022-mutex.cpp:10
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()
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