C++ Library Extensions 2022.12.09
To help learn modern C++ programming
041-array_malloc.cpp
Go to the documentation of this file.
1#include <tpf_output.hpp>
3
4#include <cstdlib>
5
8
9template<typename ElementType>
11{
12public:
13
14 using iterator = ElementType*;
15 using const_iterator = const ElementType*;
16
17private:
18 size_t m_size;
19 ElementType *m_ptr;
20
21 inline ElementType* new_alloc(size_t size)
22 { return static_cast<ElementType*>(malloc(sizeof(ElementType)*size));}
23
24 void free_memory()
25 {
26 if (this->m_size != 0 || m_ptr != nullptr)
27 {
28 free(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 iterator begin() { return &this->m_ptr[0]; }
43 iterator end() { return &this->m_ptr[this->m_size]; }
44
46 { return static_cast<const_iterator>(&this->m_ptr[0]); }
47
49 { return static_cast<const_iterator>(&this->m_ptr[this->m_size]); }
50
51 auto rbegin() { return std::reverse_iterator(end()); }
52 auto rend() { return std::reverse_iterator(begin()); }
53
54 auto crbegin() { return std::reverse_iterator(cend()); }
55 auto crend() { return std::reverse_iterator(cbegin()); }
56
57
58 size_t size() const { return this->m_size; }
59
60 void resize(size_t count)
61 {
62 if(count < 1)
63 Tpf_ThrowDebugException("count should be greater than 0");
64 else if(count == this->m_size)
65 return;
66 else
67 {
68 // free existing memory
69 this->free_memory();
70
71 try
72 {
73 this->m_size = count;
74 this->m_ptr = new_alloc(this->m_size);
75 }
76 catch(...)
77 {
78 Tpf_ThrowDebugException("Dynamic Allocation Failed");
79 }
80 }
81 }
82
83 // this is dangerous practice
84 // if dynamic allocation fails,
85 // this constructor throws std::bad_alloc exception
86 explicit dynamic_array_malloc(size_t size = 1)
87 : m_size{size}
88 {
89 this->m_ptr = new_alloc(size);
90
91 if(!this->m_ptr)
92 Tpf_ThrowDebugException("Dynamic Allocation Failed");
93 }
94
95 // if dynamic allocation fails,
96 // it throws exception
97 dynamic_array_malloc(const dynamic_array_malloc &right_hand_side) :
98 m_size{right_hand_side.m_size}
99 {
100 stream << "Copy constructor called" << endl;
101
102 try
103 {
104 this->m_ptr = this->m_size > 0 ? new_alloc(this->m_size) : nullptr;
105
106 if (m_size > 0)
107 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
108 }
109 catch(...)
110 {
111 // it reports details about allocation failure
112 Tpf_ThrowDebugException("Dynamic Allocation Failed");
113 }
114
115 }
116
117 ElementType *operator&() { return this->m_ptr; }
118
119 ElementType &operator[](size_t index) { return this->m_ptr[index]; }
120
121 const ElementType &operator[](size_t index) const { return this->m_ptr[index]; }
122
123 ElementType &at(size_t index)
124 {
125 if (index < this->m_size && this->m_ptr != nullptr)
126 return this->m_ptr[index];
127 else
128 Tpf_ThrowDebugException("Index Out of Range");
129 }
130
131 const ElementType &at(size_t index) const
132 {
133 if (index < this->m_size && this->m_ptr != nullptr)
134 return this->m_ptr[index];
135 else
136 Tpf_ThrowDebugException("Index Out of Range");
137 }
138
139 // if allocation fails,
140 // it can throw std::bad_alloc exception
142 {
143 stream << "Copy assignment operator() called" << endl;
144
145 // this code is wrong, because address of operator&()
146 // is defined for class dynamic_array_malloc,
147 // so, we cannot use the following code any longer
148 // if(this != &right_hand_side)
149
150 if (this != std::addressof(right_hand_side))
151 {
152 if (this->m_size == right_hand_side.m_size)
153 {
154 // we do not need to reallocate memory
155 // and also, we do not need to free memory
156 // this->free_memory()
157
158 if (m_size > 0)
159 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
160 }
161 else
162 {
163 // we have free existing memory
164 this->free_memory();
165 this->m_size = right_hand_side.m_size;
166
167 if (this->m_size > 0)
168 {
169 try
170 {
171 // and we have to reallocate new memory
172 this->m_ptr = new_alloc(this->m_size);
173 std::memcpy(this->m_ptr, right_hand_side.m_ptr, m_size * sizeof(ElementType));
174 }
175 catch(...)
176 {
177 // it reports details about allocation failture
178 Tpf_ThrowDebugException("Dynamic Allocation Failed");
179 }
180 }
181 }
182 }
183
184 return *this;
185 }
186
187 dynamic_array_malloc(dynamic_array_malloc &&right_hand_side) noexcept
188 : m_size{right_hand_side.m_size},
189 m_ptr{right_hand_side.m_ptr}
190 {
191 stream << "Move constructor called" << endl;
192
193 // IMPORTANT: invalidate right_hand_side
194 // after move, right_hand_side is invalide
195 // so, we should NOT access to right_hand_side after move operation
196 right_hand_side.invalidate();
197 }
198
200 {
201 stream << "Move assignment operator() called" << endl;
202
203 if (this != std::addressof(right_hand_side))
204 {
205 // delete existing memory
206 this->free_memory();
207 this->m_size = right_hand_side.m_size;
208 this->m_ptr = right_hand_side.m_ptr;
209
210 // IMPORTANT: invalidate right_hand_size after move assignment
211 // since after move operation, right_hand_side is invalid
212 // so, we should NOT access to right_hand_side after move operation
213 right_hand_side.invalidate();
214 }
215
216 return *this;
217 }
218
220 {
221 this->free_memory();
222 }
223
224 friend std::ostream& operator<<(std::ostream& os, const dynamic_array_malloc& da)
225 {
226 if(da.size() < 1)
227 os << "{ }";
228 else
229 {
230 size_t size = da.size() - 1;
231 os << "{ ";
232
233 for(size_t i = 0; i < size; ++i)
234 os << da[i] << ", ";
235
236 os << da[size] << " }";
237 }
238
239 return os;
240 }
241};
242
244{
246
247 for(size_t i = 0; i < a.size(); ++i)
248 a[i] = (int)i;
249
250 stream << "a = " << a << endl;
251}
252
254{
255 size_t count = 2;
256
257 std::vector< dynamic_array_malloc<int> > jagged_array(count); // don't do this
258
259 stream << "At this point, default constructor of dynamic_array_malloc is called "
260 << count << " times.\n"<< endl;
261
262 for(size_t i = 0; i < jagged_array.size(); ++i)
263 jagged_array[i] = dynamic_array_malloc<int>{ i + 1 };
264
265 stream << "In the for loop, default constructor of dynamic_array_malloc is called "
266 << count << " times, and move constructor is called " << count << " times\n" << endl;
267
268 stream << "So, destructor is called total: " << (count + count) << " times " << endl;
269}
270
272{
273 stream << "\nthis is better, but not perfect!!\n" << endl;
274
275 size_t count = 2;
276
277 std::vector< dynamic_array_malloc<int> > jagged_array;
278 jagged_array.reserve(count);
279
280 for(size_t i = 0; i < jagged_array.capacity(); ++i)
281 jagged_array.emplace_back(dynamic_array_malloc<int>{ i + 1 });
282}
283
284// we never created temporary dynamic array in this function
286{
287 stream << "\nthis is PEREFECT WAY, YOU SHOULD ALWAYS USE THIS METHOD!!\n" << endl;
288
289 size_t count = 10;
290
291 std::vector< dynamic_array_malloc<int> > jagged_array;
292 jagged_array.reserve(count);
293
294 for(size_t i = 0; i < jagged_array.capacity(); ++i)
295 {
296 jagged_array.emplace_back(i + 1);
297 // i + 1 is argument for dynamic_array_malloc.
298
299 // now std::vector creates dynamic_array_malloc
300 // internally, so, no move... only count times of default
301 // constructor of dynamic_array_malloc is called.
302
303 for(size_t j = 0; j < jagged_array.back().size(); ++j)
304 jagged_array.back()[j] = (int)j;
305 }
306
307 // note that the constructor of dynamic_array_malloc{size_t}
308
309 for(auto& da: jagged_array)
310 {
311 stream << da << endl;
312 }
313}
314
316{
318
319 int i = 0;
320 for(auto& e: array)
321 e = i++;
322
323 stream << array << endl;
324
325 for_each(array.crbegin(), array.crend(), [](auto e)
326 {
327 std::cout << e << std::endl;
328 });
329}
330
332{
333 size_t test_count = 5;
334 size_t array_size = 100'000'000;
335
337 for(size_t count = 0; count < test_count; ++count)
338 {
339 std::vector<int> v; v.reserve(array_size);
340 for(size_t i = 0; i < array_size; ++i)
341 v.emplace_back((int)i);
342 }
343 stream << "vector - elapsed: " << sw.elapsed_time() << endl;
344
345 for(size_t count = 0; count < test_count; ++count)
346 {
348 for(size_t i = 0; i < array_size; ++i)
349 v[i] = (int)i;
350 }
351 stream << "dynamic - elapsed: " << sw.elapsed_time() << endl;
352
353}
354int main()
355{
356
357 // do_not_do_this();
358
359 // is_still_is_not_perfect();
360
361 // please_use_this_method();
362
363 // test_iterator();
365}
std::atomic< int > count
Definition: 022-mutex.cpp:10
tpf::sstream stream
void do_not_do_this()
void test_iterator()
auto endl
void please_use_this_method()
void is_still_is_not_perfect()
int main()
void performance_test()
void examples_for_dynamic_array_malloc()
const ElementType & operator[](size_t index) const
void resize(size_t count)
ElementType & operator[](size_t index)
friend std::ostream & operator<<(std::ostream &os, const dynamic_array_malloc &da)
dynamic_array_malloc & operator=(const dynamic_array_malloc &right_hand_side)
dynamic_array_malloc(const dynamic_array_malloc &right_hand_side)
dynamic_array_malloc(dynamic_array_malloc &&right_hand_side) noexcept
ElementType & at(size_t index)
ElementType * operator&()
const ElementType * const_iterator
const_iterator cend()
const_iterator cbegin()
dynamic_array_malloc & operator=(dynamic_array_malloc &&right_hand_side) noexcept
dynamic_array_malloc(size_t size=1)
const ElementType & at(size_t index) const
std::string elapsed_time(bool bReset=true, TimeUnit dummy_time=TimeUnit{}) const
constexpr size_t array_size(ElementType(&array)[ElementSize]) noexcept
Definition: tpf_types.hpp:1240
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