-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMatrix.hpp
336 lines (314 loc) · 19.9 KB
/
Matrix.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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#include "Matrix.h"
#pragma once
using namespace linalg;
// Конструктор с 2 параметрами
template<typename T, typename Alloc>
inline Matrix<T, Alloc>::Matrix(const size_t rows, const size_t columns, Alloc alloc)
: m_alloc(alloc)
{
T* ptr_temp = m_alloc.allocate(rows * columns); // Просто выделили память
size_t i = 0; // глобальный индекс (чтобы вне try использовать)
try { // с помощью него буду отслеживать на каком я конструкторе
for (; i < rows * columns; ++i) // размещающий new
m_alloc.construct(ptr_temp + i); // запускает дефолтный конструктор
}
catch (...) { // в каком-то из конструкторов выбросилось исключение?!
for (size_t j = 0; j != i; ++j) // пройдёмся по всем уже созданным объектам
m_alloc.destroy(ptr_temp + j); // и запустим у них деструкторы
m_alloc.deallocate(ptr_temp); // освободим память
throw; // прокинем исключение дальше
}
// теперь память успешно выделена, можно её сохранить и поменять размер
m_ptr = ptr_temp;
m_rows = rows;
m_columns = columns;
m_capacity = m_rows * m_columns;
}
// Инициализировать вектор (вертикальный столбец)
template<typename T, typename Alloc>
template<typename Other>
inline Matrix<T, Alloc>::Matrix(std::initializer_list<Other> init_list, Alloc alloc)
: m_alloc(alloc)
{
typedef typename std::initializer_list<Other>::const_iterator const_iter;
T* ptr_temp = m_alloc.allocate(init_list.size()); // Просто выделили память
size_t i = 0; // глобальный индекс (чтобы вне try использовать)
try { // с помощью него буду отслеживать на каком я конструкторе
for (const_iter c_it = std::cbegin(init_list); c_it != std::cend(init_list); ++c_it) {
m_alloc.construct(ptr_temp + i, *c_it); // запускает конструктор с параметрами
++i;
}
}
catch (...) { // в каком-то из конструкторов выбросилось исключение?!
for (size_t j = 0; j != i; ++j) // пройдёмся по всем уже созданным объектам
m_alloc.destroy(ptr_temp + j); // и запустим у них деструкторы
m_alloc.deallocate(ptr_temp); // освободим память
throw; // прокинем исключение дальше
}
// теперь память успешно выделена, можно её сохранить и поменять размер
m_ptr = ptr_temp;
m_rows = init_list.size();
m_columns = 1;
m_capacity = m_rows * m_columns;
}
// Инициализировать матрицу initializer_list сосотящий из initializer_list
template<typename T, typename Alloc>
template<typename Other>
inline Matrix<T, Alloc>::Matrix(std::initializer_list<std::initializer_list<Other>> list_list, Alloc alloc)
: m_alloc(alloc)
{
typedef typename std::initializer_list<Other>::const_iterator const_iter;
typedef typename std::initializer_list<std::initializer_list<Other>>::const_iterator const_iter_global;
const size_t rows = list_list.size();
const size_t columns = list_list.begin()->size();
for (const_iter_global c_it_gl = std::cbegin(list_list); c_it_gl != std::cend(list_list); ++c_it_gl) {
if (c_it_gl->size() != columns) throw std::runtime_error("Error: different size of columns in different rows!");
}
T* ptr_temp = m_alloc.allocate(rows * columns); // Просто выделили память
size_t i = 0; // глобальный индекс (чтобы вне try использовать)
try { // с помощью него буду отслеживать на каком я конструкторе
for (const_iter_global c_it_gl = std::cbegin(list_list); c_it_gl != std::cend(list_list); ++c_it_gl) {
for (const_iter c_it = c_it_gl->begin(); c_it != c_it_gl->end(); ++c_it) {
m_alloc.construct(ptr_temp + i, *c_it); // запускает конструктор с параметрами
++i;
}
}
}
catch (...) { // в каком-то из конструкторов выбросилось исключение?!
for (size_t j = 0; j != i; ++j) // пройдёмся по всем уже созданным объектам
m_alloc.destroy(ptr_temp + j); // и запустим у них деструкторы
m_alloc.deallocate(ptr_temp); // освободим память
throw; // прокинем исключение дальше
}
// теперь память успешно выделена, можно её сохранить и поменять размер
m_ptr = ptr_temp;
m_rows = rows;
m_columns = columns;
m_capacity = m_rows * m_columns;
}
// Конструктор копирования
template<typename T, typename Alloc>
inline Matrix<T, Alloc>::Matrix(const Matrix& object)
: m_alloc(object.m_alloc)
{
const size_t rows = object.get_rows();
const size_t columns = object.get_columns();
T* ptr_temp = m_alloc.allocate(rows * columns); // Просто выделили память
size_t i = 0; // глобальный индекс (чтобы вне try использовать)
try { // с помощью него буду отслеживать на каком я конструкторе
for (size_t row = 0; row < rows; ++row) {
for (size_t column = 0; column < columns; ++column) { // размещающий new
m_alloc.construct(ptr_temp + i, object(row, column)); // запускает конструктор с параметрами
++i;
}
}
}
catch (...) { // в каком-то из конструкторов выбросилось исключение?!
for (size_t j = 0; j != i; ++j) // пройдёмся по всем уже созданным объектам
m_alloc.destroy(ptr_temp + j); // и запустим у них деструкторы
m_alloc.deallocate(ptr_temp); // освободим память
throw; // прокинем исключение дальше
}
// теперь память успешно выделена, можно её сохранить и поменять размер
m_ptr = ptr_temp;
m_rows = rows;
m_columns = columns;
m_capacity = m_rows * m_columns;
}
// Конструктор копирования от матрицы другого типа
template<typename T, typename Alloc>
template<typename Other, typename Alloc_Other>
inline Matrix<T, Alloc>::Matrix(const Matrix<Other, Alloc_Other>& object) {
const size_t rows = object.get_rows();
const size_t columns = object.get_columns();
T* ptr_temp = m_alloc.allocate(rows * columns); // Просто выделили память
size_t i = 0; // глобальный индекс (чтобы вне try использовать)
try { // с помощью него буду отслеживать на каком я конструкторе
for (size_t row = 0; row < rows; ++row) {
for (size_t column = 0; column < columns; ++column) { // размещающий new
m_alloc.construct(ptr_temp + i, static_cast<T>(object(row, column))); // запускает конструктор с параметрами
++i;
}
}
}
catch (...) { // в каком-то из конструкторов выбросилось исключение?!
for (size_t j = 0; j != i; ++j) // пройдёмся по всем уже созданным объектам
m_alloc.destroy(ptr_temp + j); // и запустим у них деструкторы
m_alloc.deallocate(ptr_temp); // освободим память
throw; // прокинем исключение дальше
}
// теперь память успешно выделена, можно её сохранить и поменять размер
m_ptr = ptr_temp;
m_rows = rows;
m_columns = columns;
m_capacity = m_rows * m_columns;
}
// Перемещающий конструктор
template<typename T, typename Alloc>
inline Matrix<T, Alloc>::Matrix(Matrix&& object) noexcept {// исключения точно не вылетят (тут неоткуда)
std::swap(m_ptr, object.m_ptr);
std::swap(m_columns, object.m_columns);
std::swap(m_rows, object.m_rows);
std::swap(m_capacity, object.m_capacity);
std::swap(m_alloc, object.m_alloc);
} // теперь во временном объекте наши старые ресурсы => они умрут снаружи
// Деструктор
template<typename T, typename Alloc>
inline Matrix<T, Alloc>::~Matrix()noexcept {
for (size_t i = 0; i < m_rows * m_columns; ++i)// пройдёмся по всем своим объектам
m_alloc.destroy(m_ptr + i); // и запустим у них деструкторы
m_alloc.deallocate(m_ptr); // освободим память
}
// Оператор присваивания копирующий
template<typename T, typename Alloc>
inline Matrix<T, Alloc>& linalg::Matrix<T, Alloc>::operator = (const Matrix& object) {
if (m_ptr == object.m_ptr) return *this; // Проверка на самоприсваивание
if (object.size() > m_capacity) { // придётся перевыделять память
*this = Matrix(object); // скопировали объект во временный и своровали у него ресурсы с помощью перемещения
// теперь старые ресурсы нашего класса у временного и они подчистятся, когда закончится инструкция (т.е. уже тут)
}
else { // иначе: вместимости моей матрицы хватит на актуальный размер объекта
size_t i = 0; // с помощью этого индекса пройдёмся по всем элементам
if (size() <= object.size()) { // т.е. актуальный размер имеющейся матрицы увеличится или не изменится
for (; i < size(); ++i)
m_ptr[i] = static_cast<T>(object.m_ptr[i]); // присваивание может выкинуть исключение, но это не нарушит целостность объекта
try { // нужно проконтролировать работу конструкторов у новых объектов
for (; i < object.size(); ++i)
m_alloc.construct(m_ptr + i, static_cast<T>(object.m_ptr[i]));
}
catch (...) { // если при создании новых объектов вылетело исключение, то эти новые надо разрушить
for (size_t j = size(); j != i; ++j)
m_alloc.destroy(m_ptr + j); // запуск деструкторов
throw;
}
}
else { // т.е. актуальный размер имеющейся матрицы уменьшится
for (; i < object.size(); ++i)
m_ptr[i] = static_cast<T>(object.m_ptr[i]);
for (; i < size(); ++i) // не нужно контролировать работу деструкторов т.к. они не должны выкидывать исключения
m_alloc.destroy(m_ptr + i); // разрушим не нужные нам элементы
}
// актуальные размеры матрицы изменились:
m_rows = object.get_rows();
m_columns = object.get_columns();
// НО capactiy не изменилось т.к. память не перевыделяли
}
return *this;
}
// Оператор присваивания копирующий от матрицы другого типа
template<typename T, typename Alloc>
template<typename Other, typename Alloc_Other>
inline Matrix<T, Alloc>& linalg::Matrix<T, Alloc>::operator = (const Matrix<Other, Alloc_Other>& object) {
// Нет смысла делать проверку на самоприсваивание т.к. там матрица другого типа => она точно другая
if (object.size() > m_capacity) { // придётся перевыделять память
*this = Matrix(object); // скопировали объект во временный и своровали у него ресурсы с помощью перемещения
// теперь старые ресурсы нашего класса у временного и они подчистятся, когда закончится инструкция (т.е. уже тут)
}
else { // иначе: вместимости моей матрицы хватит на актуальный размер объекта
size_t i = 0; // с помощью этого индекса пройдёмся по всем элементам
if (size() <= object.size()) { // т.е. актуальный размер имеющейся матрицы увеличится или не изменится
for (; i < size(); ++i)
m_ptr[i] = static_cast<T>(object.m_ptr[i]); // присваивание может выкинуть исключение, но это не нарушит целостность объекта
try { // нужно проконтролировать работу конструкторов у новых объектов
for (; i < object.size(); ++i)
m_alloc.construct(m_ptr + i, static_cast<T>(object.m_ptr[i]));
}
catch (...) { // если при создании новых объектов вылетело исключение, то эти новые надо разрушить
for (size_t j = size(); j != i; ++j)
m_alloc.destroy(m_ptr + j); // запуск деструкторов
throw;
}
}
else { // т.е. актуальный размер имеющейся матрицы уменьшится
for (; i < object.size(); ++i)
m_ptr[i] = static_cast<T>(object.m_ptr[i]);
for (; i < size(); ++i) // не нужно контролировать работу деструкторов т.к. они не должны выкидывать исключения
m_alloc.destroy(m_ptr + i); // разрушим не нужные нам элементы
}
// актуальные размеры матрицы изменились:
m_rows = object.get_rows();
m_columns = object.get_columns();
// НО capactiy не изменилось т.к. память не перевыделяли
}
return *this;
}
// Оператор присваивания перемещающий
template<typename T, typename Alloc>
inline Matrix<T, Alloc>& linalg::Matrix<T, Alloc>::operator = (Matrix&& object) noexcept {
std::swap(m_ptr, object.m_ptr);
std::swap(m_columns, object.m_columns);
std::swap(m_rows, object.m_rows);
std::swap(m_capacity, object.m_capacity);
std::swap(m_alloc, object.m_alloc);
return *this;
} // теперь во временном объекте наши старые ресурсы => они умрут снаружи
// Оператор присваивающего суммирования c матрицей другого типа
template<typename T, typename Alloc>
template<typename Other, typename Alloc_Other>
inline Matrix<T, Alloc>& Matrix<T, Alloc>::operator += (const Matrix<Other, Alloc_Other>& object) {
if (m_rows != object.m_rows || m_columns != object.m_columns)
throw std::runtime_error("Error: different shapes for operator +=!");
for (size_t row = 0; row < object.m_rows; ++row) {
for (size_t col = 0; col < object.m_columns; ++col) {
this->operator()(row, col) += static_cast<T>(object(row, col));// Оператор += между типами Т и Other => может выкинуть исключение
} // но это не нарушит целостность объекта (размер задан, значения заполнены)
}
return *this;
}
// Оператор присваивающего вычитания c матрицей другого типа
template<typename T, typename Alloc>
template<typename Other, typename Alloc_Other>
inline Matrix<T, Alloc>& Matrix<T, Alloc>::operator -= (const Matrix<Other, Alloc_Other>& object) {
if (m_rows != object.m_rows || m_columns != object.m_columns)
throw std::runtime_error("Error: different shapes for operator -=!");
for (size_t row = 0; row < object.m_rows; ++row) {
for (size_t col = 0; col < object.m_columns; ++col) {
this->operator()(row, col) -= static_cast<T>(object(row, col)); // Оператор -= между типами Т и Other => может выкинуть исключение
} // но это не нарушит целостность объекта (размер задан, значения заполнены)
}
return *this;
}
// Оператор присваивающего умножения на число другого типа
template<typename T, typename Alloc>
template<typename Other>
inline Matrix<T, Alloc>& Matrix<T, Alloc>::operator *= (const Other& value) {
for (size_t col = 0; col < m_columns; ++col) {
for (size_t row = 0; row < m_rows; ++row) {
this->operator()(row, col) *= static_cast<T>(value); // Оператор *= между типами Т и Other => может выкинуть исключение
} // но это не нарушит целостность объекта (размер задан, значения заполнены)
}
return *this;
}
// Оператор присваивающего умножения на матрицу другого типа
template<typename T, typename Alloc>
template<typename Other, typename Alloc_Other>
inline Matrix<T, Alloc>& Matrix<T, Alloc>::operator *= (const Matrix<Other, Alloc_Other>& object) {
if (m_columns != object.m_rows)
throw std::runtime_error("Error: bad shapes for operator *=!");
Matrix obj_new(m_rows, object.m_columns); // пустая матрица, т.е. элементы которой построены с помощью дефолтных конструкторов
for (size_t col = 0; col < object.m_columns; ++col) {
for (size_t row = 0; row < m_rows; ++row) {
for (size_t term = 0; term < m_columns; ++term) {
obj_new(row, col) += this->operator()(row, term) * object(term, col);
}
}
}
*this = std::move(obj_new); // теперь в obj_new наши старые ресурсы, которые "умрут" при выходе из тела оператора
return *this;
}
// оператор вызова функции (принимает два аргумента будто [i][j])
template<typename T, typename Alloc>
inline T& Matrix<T, Alloc>::operator() (size_t row, size_t column) {
if (!m_ptr) throw std::runtime_error("Error: operator () asked for element of empty matrix!");
if (m_rows <= row) throw std::runtime_error("Error: operator () asked for element: bad row!");
if (m_columns <= column) throw std::runtime_error("Error: operator () asked for element: bad column!");
return m_ptr[row * m_columns + column];
}
// оператор вызова функции для константной матрицы
template<typename T, typename Alloc>
inline const T& Matrix<T, Alloc>::operator() (size_t row, size_t column) const {
if (!m_ptr) throw std::runtime_error("Error: operator () asked for element of empty matrix!");
if (m_rows <= row) throw std::runtime_error("Error: operator () asked for element: bad row!");
if (m_columns <= column) throw std::runtime_error("Error: operator () asked for element: bad column!");
return m_ptr[row * m_columns + column];
}