Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
1

ООП. Возникает ошибка с динамической памятью

11.04.2023, 00:36. Показов 2081. Ответов 49

Author24 — интернет-сервис помощи студентам
/*Доброй ночи! Возникает ошибка с памятью в деструкторе класса Person (для двух других классов все срабатывает хорошо). Такое ощущение, что уже происходило освобождение памяти для переменных класса Person (FIO и Birthday), и она пытается освободить память по старому адресу, которого уже нет. */


C++
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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
//Задание. Создайте программу, имитирующую многоквартирный дом. Необходимо иметь классы “Человек”, “Квартира”, “Дом”.
//Класс “Квартира” содержит динамический массив объектов класса “Человек”. Класс “Дом” содержит массив объектов класса “Квартира”.
//Каждый из классов содержит переменные - члены и функции - члены, которые необходимы для предметной области класса.
//Обращаем ваше внимание, что память под строковые значения выделяется динамически. Например, для ФИО в классе “Человек”.
//Не забывайте обеспечить классы различными конструкторами (конструктор копирования обязателен), деструкторами.
//В main протестировать работу полученного набора классов.
 
#include <iostream>
#include <Windows.h>
#define cout std::cout
#define endl std::endl
#define cin std::cin
 
typedef unsigned short US;
 
class Person {
 
public:
    char* FIO;
    char* Birthday;
    US Age;
 
    Person() :FIO(nullptr), Birthday(nullptr), Age(0){}
    Person(char* fio, char* bday, US age) : FIO(new char[strlen(fio) + 1]), Birthday(new char[strlen(bday) + 1]), Age(age) {
        if (!FIO || !Birthday) {
            return;
        }
 
        strcpy_s(FIO, strlen(fio) + 1, fio);
        strcpy_s(Birthday, strlen(bday) + 1, bday);
    }
 
    ~Person() { 
        
        cout << "\nВызывался деструктор Person " << endl;
 
        if (FIO)
            delete[]FIO;
        FIO = nullptr; 
        if (Birthday)
            delete[]Birthday;
        Birthday = nullptr;
    }
 
    Person(const Person& an) : Age(an.Age) {
 
        if (FIO) {
            delete[]FIO; FIO = nullptr;
        }
 
        US lenfio = strlen(an.FIO) + 1;
 
        if (!lenfio) {
            return;
        }
 
        FIO = new char[lenfio];
 
        if (FIO == nullptr) {
            return;
        }
 
        strcpy_s(FIO, lenfio, an.FIO);
 
        if (Birthday) {
            delete[]Birthday; Birthday = nullptr;
        }
 
        US lenbday = strlen(an.Birthday) + 1;
 
        if (!lenbday) {
            return;
        }
 
        Birthday = new char[lenbday];
 
        if (Birthday == nullptr) {
            return;
        }
 
        strcpy_s(Birthday, lenbday, an.Birthday);
    }
 
    Person& operator = (const Person& an) {
 
        if (this == &an) {
            return *this;
        }
 
        Age = an.Age;
 
        if (FIO) {
            delete[]FIO; FIO = nullptr;
        }
 
        US lenfio = strlen(an.FIO) + 1;
 
        if (!lenfio) {
            return *this;
        }
 
        FIO = new char[lenfio];
 
        if (FIO == nullptr) {
            return *this;
        }
 
        strcpy_s(FIO, lenfio, an.FIO);
 
        if (Birthday) {
            delete[]Birthday; Birthday = nullptr;
        }
 
        US lenbday = strlen(an.Birthday) + 1;
 
        if (!lenbday) {
            return *this;
        }
 
        Birthday = new char[lenbday];
 
        if (Birthday == nullptr) {
            return *this;
        }
 
        strcpy_s(Birthday, lenbday, an.Birthday);
 
        return *this;
    }
 
    Person(Person&& an) {
 
        Age = an.Age;
        an.Age = 0;
        FIO = an.FIO;
        an.FIO = nullptr;
        Birthday = an.Birthday;
        an.Birthday = nullptr;
    }
 
    Person& operator=(Person&& an) {
 
        if (this == &an) {
            return *this;
        }
 
        if (FIO) { delete[]FIO; FIO = nullptr; }
        if (Birthday) { delete[]Birthday; Birthday = nullptr; }
 
        Age = an.Age;
        an.Age = 0;
        FIO = an.FIO;
        an.FIO = nullptr;
        Birthday = an.Birthday;
        an.Birthday = nullptr;
 
        return *this;
    }
 
    inline void Fill();
 
    inline void Print()const;
};
 
class Flat {
    Person* human;
    US size;
    US num;
    US room;
 
public: 
    Flat() : human(nullptr), size(0), num(0), room(1) {}
    Flat(US an, US n, US r) : size(an), human(new Person[size]), num(n), room(r) {}
    ~Flat() {
 
        cout << "\nВызывался деструктор Flat " << endl;
 
        if (human)
            delete[]human;
        human = nullptr;
    }
    Flat(const Flat& an) : size(an.size), human(new Person[size]), num(an.num), room(an.room) {
        if (human == nullptr) {
            return;
        }
 
        for (US i = 0; i < size; ++i) {
            human[i] = an.human[i];
        }
    }
    Flat& operator = (const Flat& an) {
        if (this == &an) {
            return *this;
        }
 
        if (human) { delete[]human; human = nullptr; }
 
        size = an.size;
        num = an.num;
        room = an.room;
 
        human = new Person[size];
        
        if (human == nullptr) {
            return *this;
        }
 
        for (US i = 0; i < size; ++i) {
            human[i] = an.human[i];
        }
 
        return *this;
 
 
    }
    Flat(Flat&& an) {
        size = an.size;
        an.size = 0;
        num = an.num;
        an.num = 0;
        room = an.room;
        an.room = 0;
        human = an.human;
        an.human = nullptr;
    }
    Flat& operator = (Flat&& an) {
        if (this == &an) {
            return *this;
        }
 
        size = an.size;
        an.size = 0;
        num = an.num;
        an.num = 0;
        room = an.room;
        an.room = 0;
        human = an.human;
        an.human = nullptr;
 
        return *this;
    }
 
    void Fill();
    inline void Print()const;
};
 
class House {
    Flat* apart;
    US size;
 
public:
    House() :apart(nullptr){}
    House(US an) : size(an), apart(new Flat[size]) {}
    ~House() {
 
        cout << "\nВызывался деструктор House " << endl;
 
        if (apart)
            delete[]apart;
        apart = nullptr;
    }
    House(const House& an) : size(an.size) {
        if (!apart) {
            apart = new Flat[size];
        }
 
        if (apart) {
 
            for (US i = 0; i < size; ++i) {
                apart[i] = an.apart[i];
            }
        }
    }
    House& operator = (const House& an) {
        if (this == &an) {
            return *this;
        }
 
        size = an.size;
 
        if (apart) { delete[]apart; apart = nullptr; }
 
        apart = new Flat[size];
 
        if (apart == nullptr) {
            return *this;
        }
 
        for (US i = 0; i < size; ++i) {
            apart[i] = an.apart[i];
        }
 
        return *this;
    }
    House(House&& an) {
        size = an.size;
        an.size = 0;
        apart = an.apart;
        an.apart = nullptr;
    }
    House& operator = (House&& an) {
        if (this == &an) {
            return *this;
        }
 
        size = an.size;
        an.size = 0;
 
        if (apart) { delete[]apart; apart = nullptr; }
 
        apart = an.apart;
        an.apart = nullptr;
 
        return *this;
    }
 
    void Fill();
    inline void Print()const;
};
 
 
int main() {
 
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    {
        House TestHouse;
 
        TestHouse.Fill();
 
        cout << "\n\t\t\t\t\tИнформация о доме: " << endl;
 
        TestHouse.Print();
    }
 
    system("pause");
    return 0;
}
 
inline void Person::Fill() {
 
    if (!FIO) {
 
        FIO = new char[70];
 
    }
 
    if (FIO) {
 
        cout << "\nВведите ФИО жителя: ";
        gets_s(FIO, strlen(FIO) + 1);
    }
 
    if (!Birthday) {
 
        Birthday = new char[15];
    }
 
    if (Birthday) {
        cout << "\nВведите дату его рождения (формат - ХХ.ХХ.ХХХХ) - ";
        gets_s(Birthday, strlen(Birthday) + 1);
    }
 
    if (strlen(Birthday) < 10 || strlen(Birthday) > 10) { cout << "\nОшибка ввода данных! " << endl; return; }
 
    if (!FIO || !Birthday) { return; }
 
    cout << "\nВведите возраст (полных лет. Если проживает ребенок, которому менее года, укажите 1): ";
    cin >> Age;
    cin.get();
 
    if (Age < 1 || Age > 120) {
 
        cout << "\nОшибка ввода данных! " << endl;
        return;
    }
 
    cout << endl;
 
}
 
inline void Person::Print()const {
 
    if (!FIO || !Birthday) { return; }
 
    cout << "\nФИО жителя - " << FIO << endl;
    cout << "\nДата рождения - " << Birthday << endl;
    cout << "\nВозраст - " << Age << endl;
 
}
 
void Flat::Fill() {
    
    cout << "Введите номер квартиры: ";
    cin >> num;
    cin.get();
 
    if (num < 1 || num > 2000) { cout << "\nОшибка ввода данных! " << endl; return; }
 
    cout << "\nСколько человек в ней проживают? ";
    cin >> size;
    cin.get();
 
    cout << "\nСколько в ней комнат? ";
    cin >> room;
    cin.get();
 
    if (room < 1 || room > 50) { cout << "\nОшибка ввода данных! " << endl; return; }
 
    if (!human) {
 
        human = new Person[size];
    }
 
    if (human == nullptr) {
 
        return;
    }
 
    cout << endl;
 
    for (US i = 0; i < size; ++i) {
        cout << i + 1 << ") ";
        human[i].Fill();
    }
    
 
}
 
inline void Flat::Print()const {
 
    cout << "\nНомер квартиры: " << num << ", квартира " << room << "-комнатная, в ней проживает - " << size << " чел. " << endl;
 
    if (human) {
 
        for (US i = 0; i < size; ++i) {
 
            cout << "\n" << i + 1 << ") " << endl;
            human[i].Print();
            cout << endl;
        }
    }
 
}
 
void House::Fill() {
 
    cout << "Сколько квартир в вашем доме? ";
    cin >> size;
    cin.get();
 
    if (size < 1 || size > 10000) { cout << "\nОшибка ввода данных! " << endl; return; }
 
    if (!apart) {
 
        apart = new Flat[size];
    }
 
    system("cls");
 
    for (US i = 0; i < size; ++i) {
        cout << "Введите данные о " << i + 1 << "-й квартире: " << endl << endl;
        apart[i].Fill();
        system("pause");
        system("cls");
    }
}
 
inline void House::Print()const {
 
    cout << "\nКвартир в доме - " << size << endl;
 
    if (apart) {
 
        for (US i = 0; i < size; ++i) {
            apart[i].Print();
        }
    }
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
11.04.2023, 00:36
Ответы с готовыми решениями:

Ошибка с динамической памятью
Очень прошу помочь исправить ошибку в области дин.памяти. Проблема: 1. Шаг Создаю массив для...

Почему у меня возникает ошибка с памятью (sigsegv)?
почему у меня возникает ошибка с памятью? Код прилагаю #include&lt;iostream&gt; #include&lt;vector&gt; using...

Ошибка в коде при работе с динамической памятью
Не могу понят что не так ? При вводе в динамический массив А последовательность &quot;61 3 2 0 -2 -25...

Работа с динамической памятью через указатели. Загадочная ошибка.
Пишу в Microsoft Visual Studio -&gt;Win32 Console application -&gt;C++. Есть такая задача: Создать...

Работа с динамической памятью через указатели. Загадочная ошибка.
Программа запускается и нормально исполняется, но в конце появляется ошибка. Мог бы кто подсказать...

49
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
11.04.2023, 11:48 21
Author24 — интернет-сервис помощи студентам
Цитата Сообщение от Prang Посмотреть сообщение
Я так понял, что чем больше проверок, тем безопаснее, вдруг он не нулевой будет каким-то образом ))
Там проблема в целом не в самой проверке, а том для чего эта проверка вызвана. У вас конструктор, указатель FIO только-только "народился" и значения у него еще никакого нет - мусор. А вы его проверять.
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
11.04.2023, 11:49  [ТС] 22
Цитата Сообщение от DrOffset Посмотреть сообщение
Prang, ответственно вам говорю, что "не пройдете" вы еще очень много всего. Т.е. даже если вы закончите обучение с честным красным дипломом (условно говоря), но будете опираться только на то, что "проходили", то в части многих важных вопросов у вас будут зияющие дыры в знаниях. Поэтому без самостоятельно чтения\изучения просто никак.
Я стараюсь дополнительно что-то проходить, смотрю тот же SimpleCode на ютубе и selfedu. Но увы, со временем тоже напряг и прохожу ровно столько, сколько мы проходим в учебной организации, т.е. параллельно. Естественно, я понимаю, что на учебе мне всего не дадут и очень много мне нужно будет разбираться самому. Кстати, кто думал иначе (что им в голову все будут вдалбливать, а сами они ничего искать и добывать не будут), те бросили занятия.
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
11.04.2023, 11:58 23
Цитата Сообщение от Prang Посмотреть сообщение
Кто то говорит Страуструпа новичкам читать рано
Страуструп, если "Язык программирования С++", то рано.
Если "Принципы и практика", то в самый раз, но в паре с другой книгой, например Прата или Липпмана. Эти книги не взаимозаменяемые, "Принципы и практика" Страуструпа учат "как", а Липпман и Прата "что".

Добавлено через 6 минут
Цитата Сообщение от Prang Посмотреть сообщение
Ф-ция copy только знакома
Это не та copy (это самописная copy), для внутренних нужд класса, чтобы не писать много раз одни и те же действия.
Такой принцип, знаете? Это вот самый примитивный пример этого принципа.
1
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
11.04.2023, 12:05  [ТС] 24
Цитата Сообщение от DrOffset Посмотреть сообщение
Такой принцип, знаете?
Читал где-то про этот метод, как раз в сортировках.
Но... из вашего того кода я мало, что понял... Буду разбираться сначала с ошибками изначального кода, на которые вы указали. Спасибо!
0
3719 / 2648 / 761
Регистрация: 29.06.2020
Сообщений: 9,800
11.04.2023, 15:01 25
Цитата Сообщение от Prang Посмотреть сообщение
Я так понял, что чем больше проверок, тем безопаснее
Чем больше проверок, тем медленнее

Вчера написал для Person.
Код написан что бы работал, ни на что не претендует, ничего сложного не использовал.

С небольшим тестом работоспособности.

C++
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
#include <iostream>
#include <cstring>
 
class Person {
public:
    char* FIO;
    char* Birthday;
    uint16_t Age;
 
    Person()
        : FIO(strdup(""))
        , Birthday(strdup(""))
        , Age(0){
    }
    Person(const char* fio, const char* bday, uint16_t age)
        : FIO(strcpy(new char[strlen(fio) + 1], fio))
        , Birthday(strcpy(new char[strlen(bday) + 1], bday))
        , Age(age){
    }
 
    ~Person(){
        std::cout << "Human \"" << FIO << "\" Die" << std::endl;
        delete[] FIO;
        delete[] Birthday;
    }
 
    Person(const Person& an) 
        : FIO(strcpy(new char[strlen(an.FIO) + 1], an.FIO))
        , Birthday(strcpy(new char[strlen(an.Birthday) + 1], an.Birthday))
        , Age(an.Age){
    }
    Person& operator=(const Person& an)
    {
        if (this == &an)
            return *this;
        delete [] FIO;
        FIO = strcpy(new char[strlen(an.FIO) + 1], an.FIO);
        delete [] Birthday;
        Birthday = strcpy(new char[strlen(an.Birthday) + 1], an.Birthday);
        Age = an.Age;
        return *this;
    }
    void Print() const {
        std::cout << '\"' << FIO << "\" \"" << Birthday << "\" " << Age << std::endl;
    }
};
int main(){
    Person().Print(); // test default constructor and quick "dead"
    Person p("Vasya", "1 april 1970", 65000); // test with args
    p.Print();
    (p = Person()).Print();
    (p = Person("Vova", "today", 0)).Print();
    Person p2(p);
    p.FIO[0] = 'B'; p.FIO[2] = 'b';
    p2.Print();
}
Добавлено через 1 минуту
Цитата Сообщение от SmallEvil Посмотреть сообщение
Person& operator=(const Person& an)
Конечно небезопасный совсем )

Добавлено через 42 секунды
Впрочем как и все остальное, с таким подходом.
0
Модератор
Эксперт CЭксперт С++
5287 / 2374 / 342
Регистрация: 20.02.2013
Сообщений: 5,773
Записей в блоге: 20
11.04.2023, 16:55 26
Цитата Сообщение от Prang Посмотреть сообщение
Я стараюсь дополнительно что-то проходить, смотрю тот же SimpleCode на ютубе и selfedu. Но увы, со временем тоже напряг и прохожу ровно столько, сколько мы проходим в учебной организации, т.е. параллельно. Естественно, я понимаю, что на учебе мне всего не дадут и очень много мне нужно будет разбираться самому.
Зачастую, видосы на ютубе - гораздо менее предпочтительный вариант, по сравнению с книгами и статьями. Особенно с книгами и статьями таких авторов, как Герб Саттер, Николаи Йосуттис, Скотт Мейерс, Стенли Липпман, Бьярне Страуструп, Андрей Александреску, Стивен Дьюхерст, Дэвид Вандевурд и им подобные - люди, у которых в портфолио не один десяток лет профессионального программирования, участие в комитете по стандартизации, выступления на CppCon и т. п.

К тому же, выбирая ресурс, на который Вы опираетесь в обучении, имеет смысл задуматься о качестве материала, подачи, компетенции автора и тому подобных вещах.

Обычный мамкин ютубер - это просто чел с улицы, который вчера купил себе вебку и посмотрел пару туториалов, как снимать видосы для ютуба. В то время как книга - это целая индустрия - там и компетенция автора, и ревьюеры, редакторы, которые вычитывают книгу, издательство и так далее. А особенно, когда книга от именитого автора, которая выдержала ни одно издание. Тут сразу ясно, что и глубина познаний автора заслуживают доверия, и качество материала, и так далее.

Поэтому, на мой взгляд, лучший способ учиться для начинающего - читать книги, выполнять упражнения из них. Когда поднатореете, хороший вариант - помогать новичками на форуме, читать/писать код. Когда почувствуете себя ещё более уверенно, начинайте контрибьютить в open-source проекты. В качестве итоговой проверки своих навыков попробуйте свои силы в реальной ай-ти компании в качестве разработчика.
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 00:53  [ТС] 27
Цитата Сообщение от DrOffset Посмотреть сообщение
Есть правда нарушение порядка инициализации. Компилятор разве не предупреждает?
Вы имеете ввиду, что инициализация д.б. в том же порядке, что и порядок объявления переменных в классе? Это обязательно?

Добавлено через 38 секунд
Цитата Сообщение от SmallEvil Посмотреть сообщение
Вчера написал для Person.
Спасибо большое за потраченное время. Обязательно посмотрю

Добавлено через 4 минуты
Цитата Сообщение от sourcerer Посмотреть сообщение
Зачастую, видосы на ютубе - гораздо менее предпочтительный вариант, по сравнению с книгами и статьями
Согласен, но на том же SimpleCode - свыше 300К подписчиков, а для айтишного канала (именно обучающего) это очень много, и своего рода признание.

Начал читать Страуструпа, позже мельком просмотрел Герба Сеттера - Стандарты программирования (показалась даже интереснее, особенно оглавление - напр. "Почему стоит часто употреблять const" или почему стоит избегать макросов. Спасибо!

Добавлено через 7 минут
Цитата Сообщение от DrOffset Посмотреть сообщение
// Исправление №3: fio и bday - const, всегда следим за const-корректностью
    Person(const char* fio, const char* bday, US age)
Можете пояснить, почему именно так и чем грозит использование неконст? Спасибо

Добавлено через 7 минут
Цитата Сообщение от DrOffset Посмотреть сообщение
FIO = new char[lenFio]; // №6
        Birthday = new char[lenBirthday]; // №7
А как насчет проверки на то, что память выделилась? if (!FIO || !Birthday) return;
Теоретически, она же могла не выделиться, и тогда смысл в дальнейших действиях? Или тоже бесполезная проверка?
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 00:55 28
Цитата Сообщение от Prang Посмотреть сообщение
Вы имеете ввиду, что инициализация д.б. в том же порядке, что и порядок объявления переменных в классе? Это обязательно?
В вашем случае - да, потому что у вас зависимость от этого порядка задана.
А инициализация будет в том порядке, в котором указаны поля, независимо от того, как вы их написали в списке инициализации. Поэтому ваш код содержит ошибку из-за того, что вы надеетесь на то, что прядок будет иным.

Добавлено через 1 минуту
Цитата Сообщение от Prang Посмотреть сообщение
А как насчет проверки на то, что память выделилась?
Перечитайте внимательно все, что я писал выше:
Цитата Сообщение от DrOffset Посмотреть сообщение
// Исправление №4: удаляем бесполезные проверки на nullptr: обычный new
бросает исключение при неудаче, nullptr не возвращает
.
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 00:59  [ТС] 29
Цитата Сообщение от DrOffset Посмотреть сообщение
// Замечание №5: не стоит выходить из не до конца отработавшего конструктора,
        // не предприняв действий по очистке уже выделенных ресурсов.
        // лучший способ решить эту проблему - использование RAII вместо ручного контроля
        // В этом исправленном варианте осталась проблема безопасности исключений
        // оставлена намеренно на будущий разбор, потому что исправлений и так много,
        // а это потребует значительного изменения подхода
RAII пока не проходили. Хотелось бы научиться именно ручному контролю

Добавлено через 3 минуты
Цитата Сообщение от DrOffset Посмотреть сообщение
// Исправление №4: удаляем бесполезные проверки на nullptr: обычный new
бросает исключение при неудаче, nullptr не возвращает.

А, т.е. если память в каком-то случае не выделилась, я поймаю исключение и без проверок? Понял
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 01:04 30
Цитата Сообщение от Prang Посмотреть сообщение
RAII пока не проходили.
Знаете, я бы сказал, что это вредительство. Потому что это центральная идиома, вокруг которой строится вообще все современное программирование на С++. Без шуток и преувеличений. Понимаете?

Добавлено через 2 минуты
Цитата Сообщение от Prang Посмотреть сообщение
А, т.е. если память в каком-то случае не выделилась, я поймаю исключение и без проверок? Понял
Да, в вызывающем коде поймаете. Главная мысль: конструктор должен либо полностью завершить создание объекта, либо не создавать его вовсе (выйти с исключением). Практика (как у вас было) незавершать создание объекта в конструкторе в случае ошибки и выходить из него штатно, оставляя объект в недосозданном состоянии, очень плохая.
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 01:12  [ТС] 31
Цитата Сообщение от DrOffset Посмотреть сообщение
Person(an).swap(*this);
Почему компилятор мне так не дает написать? Говорит, что swap не является членом класса Person
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 01:15 32
Цитата Сообщение от Prang Посмотреть сообщение
Почему компилятор мне так не дает написать? Говорит, что swap не является членом класса Person
Ну наверное потому, что вы забыли swap добавить в Person?
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 01:17  [ТС] 33
Цитата Сообщение от DrOffset Посмотреть сообщение
Знаете, я бы сказал, что это вредительство. Потому что это центральная идиома, вокруг которой строится вообще все современное программирование на С++. Без шуток и преувеличений. Понимаете?

Не понимаю, но верю вам. Я не удивлюсь, если мы этот RAII и не будем проходить. Нам сказали, что осталось 4 мес. обучения С++. А проучились полгода, и вот уровень. И я далеко не худший студент )

Добавлено через 1 минуту
Цитата Сообщение от DrOffset Посмотреть сообщение
Ну наверное потому, что вы забыли swap добавить в Person?
Даже стандартные ф-ции необходимо добавлять в сам класс, чтобы с ними в нем можно было работать? )
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 01:21 34
Цитата Сообщение от Prang Посмотреть сообщение
Даже стандартные ф-ции необходимо добавлять в сам класс, чтобы с ними в нем можно было работать? )
Где же они стандартные, если это семантика класса, которую я сам ввел для удобства?

Добавлено через 36 секунд
У меня же там в коде все написано. Посмотрите еще раз.
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 01:33  [ТС] 35
Цитата Сообщение от DrOffset Посмотреть сообщение
У меня же там в коде все написано. Посмотрите еще раз.
Нашел ) Я просто начал с самого начала, и иду построчно сверху вниз, а она у вас внизу реализована ) Спасибо!

А почему оператор перемещения мой приводит к утечке памяти?

Добавлено через 5 минут
Цитата Сообщение от DrOffset Посмотреть сообщение
void swap(Person& other)
    {
        std::swap(FIO, other.FIO);
        std::swap(Birthday, other.Birthday);
        std::swap(Age, other.Age);
    }
Нас так ругают делать (называть одним именем), говорят не безопасно ))
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 01:37 36
Цитата Сообщение от Prang Посмотреть сообщение
Person(an).swap(*this);
У этого приема есть название. Идиома copy-and-swap.


Цитата Сообщение от Prang Посмотреть сообщение
А почему оператор перемещения мой приводит к утечке памяти?
Вот ваш код:
Цитата Сообщение от Prang Посмотреть сообщение
C++
1
2
3
4
5
6
7
8
size = an.size;
 an.size = 0;
 num = an.num;
 an.num = 0;
 room = an.room;
 an.room = 0;
 human = an.human;
 an.human = nullptr;
Он не обменивает местами состояния объектов *this и an, вместо этого принудительно задает null для состояния an.
Если у вас в *this уже была выделена какая-то память, то вы указатели на нее перезапишете значениями из an, а в an оставите null`ы. Т.к. указатели вы перетерли, то освободить эту память теперь никак нельзя = утечка. Если бы вы сделали обмен местами, как я показывал, или другим способом, то памятью, которая у вас была в *this, стал бы заведовать объект an, что избавило бы вас от утечки.

Добавлено через 1 минуту
Цитата Сообщение от Prang Посмотреть сообщение
Нас так ругают делать (называть одним именем), говорят не безопасно ))
Ну сделайте другое имя. Это вот здесь вообще не важно.
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 01:49  [ТС] 37
Цитата Сообщение от DrOffset Посмотреть сообщение
Person(an).swap(*this);
А можете объяснить, что мы здесь делаем?

Цитата Сообщение от DrOffset Посмотреть сообщение
an.swap(*this);
И чем от этого отличается?

Ну то есть вторая вроде понятно, мы в операторе меняем значения переменных нашего класса на значения переменных экземпляра класса, который передается параметром. А первая не понятно. Сначала вызывается конструктор копирования а потом меняются значения?

Добавлено через 1 минуту
Цитата Сообщение от DrOffset Посмотреть сообщение
У этого приема есть название. Идиома copy-and-swap.
Вы предвидели мой вопрос?)

Добавлено через 2 минуты
Цитата Сообщение от DrOffset Посмотреть сообщение
Он не обменивает местами состояния объектов *this и an, вместо этого принудительно задает null для состояния an.
Если у вас в *this уже была выделена какая-то память, то вы указатели на нее перезапишете значениями из an, а в an оставите null`ы. Т.к. указатели вы перетерли, то освободить эту память теперь никак нельзя = утечка. Если бы вы сделали обмен местами, как я показывал, или другим способом, то памятью, которая у вас была в *this, стал бы заведовать объект an, что избавило бы вас от утечки.

Может быть из-за этого и был весь сыр бор. Сейчас попробую, как вы сказали и вуаля

Добавлено через 4 минуты
Цитата Сообщение от DrOffset Посмотреть сообщение
Он не обменивает местами состояния объектов *this и an, вместо этого принудительно задает null для состояния an.
Если у вас в *this уже была выделена какая-то память, то вы указатели на нее перезапишете значениями из an, а в an оставите null`ы. Т.к. указатели вы перетерли, то освободить эту память теперь никак нельзя = утечка. Если бы вы сделали обмен местами, как я показывал, или другим способом, то памятью, которая у вас была в *this, стал бы заведовать объект an, что избавило бы вас от утечки.

Ну да, тут получается даже проверка (if (!human)) не к месту, толку от нее. Кажется начинаю понимать
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 01:54 38
Цитата Сообщение от Prang Посмотреть сообщение
А можете объяснить, что мы здесь делаем?
Могу, но это также хорошо объяснено по ссылке, которую я дал выше. Прочитайте сначала там, а я подключусь, только если все равно будут затруднения.

Цитата Сообщение от Prang Посмотреть сообщение
И чем от этого отличается?
Тем, что так не будет работать. an - это константа здесь. И обменивать надо с копией, а не с исходным значением.

Добавлено через 2 минуты
Цитата Сообщение от Prang Посмотреть сообщение
Может быть из-за этого и был весь сыр бор
Сыр-бор был и-за ненужного delete внутри конструктора, который пытался освободить несуществующую память. А проверка, которая как бы должна была от этого защищать, не срабатывала, потому что фактически проверяла "мусор".
Вот это место:
Цитата Сообщение от Prang Посмотреть сообщение
C++
1
2
3
4
5
6
7
    Person(const Person& an) : Age(an.Age) {
 
        if (FIO) { 
            delete[]FIO; FIO = nullptr;
        }
 
.....................
0
18 / 1 / 0
Регистрация: 30.03.2023
Сообщений: 29
12.04.2023, 01:58  [ТС] 39
Цитата Сообщение от DrOffset Посмотреть сообщение
Person(Person&& an) : FIO(nullptr), Birthday(nullptr), Age(0)
    {
        an.swap(*this);
    }

Хотелось бы по поводу этого еще задать вопрос, последний сегодня ) А в чем отличие вашего от моего? Полагаю тоже, не меняются значение и может утечка произойти?

Добавлено через 2 минуты
Цитата Сообщение от DrOffset Посмотреть сообщение
потому что фактически проверяла "мусор".
Вот это место:
"НННННННННННННННННННННННннн" ? Типа что-то есть, поэтому и не срабатывала?
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.04.2023, 02:01 40
Prang, по сути - ничем. этот конструктор у вас был правильный, но я просто привёл к единому стилю.
0
12.04.2023, 02:01
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
12.04.2023, 02:01
Помогаю со студенческими работами здесь

После выполнения программы, работающей с динамической памятью, вылетает ошибка
при компиляции вылетает ошибка (не может обратиться к private gолям next, val) err: C2248 если...

Работа с динамической памятью. Ошибка в функции getline через посимвольный ввод
Было дано вот такое задание: В этой задаче вам необходимо написать функцию getline, которая...

Ошибка при работе с динамической памятью - 'Invalid pointer operatoion'
Столкнулся с такой проблемой - код исполняется правильно, но при закрытие программы вываливается...

Файл: при работе с динамической памятью возникает ошибка: "Cannot find bounds of current function"
Хочу считать из файла строку(до точки), выделять под неё память кусками по 50, потом её вывести....

Почему возникает ерунда с памятью?
Добрый день У меня возникла небольшая проблемка хотя ладно болшая проблемка. Пишу...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Как работать с ветками (branch) в Git
InfoMaster 17.01.2025
Система контроля версий Git произвела революцию в процессе разработки программного обеспечения, предоставив разработчикам мощный инструмент для управления изменениями в коде. Одной из наиболее важных. . .
Как откатить последние коммиты в Git
InfoMaster 17.01.2025
Система контроля версий Git стала неотъемлемой частью современной разработки программного обеспечения, предоставляя разработчикам мощные инструменты для управления изменениями в коде. Одним из. . .
Что такое boilerplate и scaffold, чем они отличаются
InfoMaster 17.01.2025
В современном мире разработки программного обеспечения эффективность и скорость создания качественного кода играют crucial роль в успехе проектов. Разработчики постоянно ищут способы оптимизировать. . .
Чем отличаются ссылки и указатели в С++
InfoMaster 17.01.2025
В современном программировании на C++ эффективная работа с памятью является ключевым аспектом разработки качественного программного обеспечения. Указатели и ссылки представляют собой два. . .
В чем разница между PUT и POST
InfoMaster 17.01.2025
В современной веб-разработке правильное использование HTTP-методов играет ключевую роль в создании надежных и эффективных API-интерфейсов. Протокол HTTP прошел долгий путь развития с момента своего. . .
DTO, POCO и Value Object: что это такое, когда и как использовать
InfoMaster 17.01.2025
Введение в паттерны передачи данных В современной разработке программного обеспечения эффективное управление данными и их передача между различными слоями приложения являются ключевыми аспектами. . .
Что такое pull request в Git
InfoMaster 17.01.2025
В современной разработке программного обеспечения pull request в Git представляет собой ключевой механизм для эффективного взаимодействия между разработчиками при работе над общим кодом проекта. По. . .
Как вернуться к предыдущему коммиту в Git
InfoMaster 17.01.2025
Система контроля версий Git представляет собой мощный инструмент для управления изменениями в программном коде, который позволяет разработчикам эффективно отслеживать и контролировать историю. . .
Что такое паттерны программировани­я и проектирования
InfoMaster 17.01.2025
Роль паттернов в современной разработке программного обеспечения В современном мире разработки программного обеспечения паттерны проектирования стали неотъемлемой частью профессионального подхода. . .
Как добавить конструктор Яндекс Карт на сайт
InfoMaster 17.01.2025
Введение в API Яндекс Карт В современной веб-разработке интеграция картографических сервисов стала неотъемлемой частью многих проектов. API Яндекс Карт представляет собой мощный инструмент для. . .
Что такое javascript:void­­(0) и зачем это нужно
InfoMaster 17.01.2025
Когда вы сталкиваетесь с веб-разработкой, особенно с использованием JavaScript, одной из директив, которая часто встречается, является javascript:void(0). Это выражение вызывает интерес из-за своей. . .
Что такое оркестрация и хореография микросервисов
InfoMaster 17.01.2025
Введение в оркестрацию и хореографию микросервисов В современном мире разработки программного обеспечения микросервисная архитектура стала ключевым подходом к созданию масштабируемых и гибких. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru