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