0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
||||||
1 | ||||||
Ошибка при создании и заполнении динамического массива указателей на объекты23.03.2022, 21:09. Показов 1129. Ответов 16
Коллеги, пишу что-то типа простенького симулятора карточной игры с активным использованием указателей и new\delete конструкций (для тренировки).
Есть вот такой код: Кликните здесь для просмотра всего текста
Суть в чём вкратце: 1. Есть класс CARD (объектов типа карта с полями достоинство и масть). Он умеет только создавать карту через различные к-торы и выводить на экран. 2. Есть класс DECK (колода из 52 карт), который представлен массивом из 52 указателей на объекты типа CARD. Он умеет инициализировать объект Колоды с помощью new CARD() к-тора т.е. заполнять колоды картами по порядку. Также умеет выводить всю колоду на экран, мешать 2 произвольные карты, а также перемешивать всю колоду. Это реализовано через перемешивание указателей. Ещё умеет вытаскивать одну карту из колоды по номеру, возвращая указатель на вытащенную карту и, соответственно, убирая её из колоды. До этого момента всё работает, как часы! 3. Дальше сделал некий класс SUBSET (произвольное кол-во карт извлекаемых из объекта колоды класса DECK). В нём сидит указатель типа CARD** т.е. pointer-to-pointer на начало массива, содержащего указатели на объекты типа CARD, а также размер выделяемого подмножества карт - size (size будет задан в к-торе объекта). К-тор этого класса с числовым параметром инициализирует поле size, а затем динамически создаёт через конструкцию new Card*[size] массив указателей на некоторое подмножество карт (скажем 6). К-тор только выделяет память, но не заполняет его указателями на реальные карты! Дальше функция данного класса SUBSET::fill(DECK), которая принимает на вход ранее созданный объект колоды, должна заполнить созданный в к-торе SUBSET массив указателей на карты. Пока что беру для заполнения объекта подмножества просто первые карты подряд из перемешанной колоды т.е. без изысков. Всё вроде заполняется и потом выводится на экран с помощью метода SUBSET::dislplay(), ОДНАКО, уже после корректного отображения всех данных появляется ошибка: "Debug Assertion Failed! (Expression:_CtrlsValidHeapPointer(Block))." Экспериментальным путём с помощью _getch() после каждой "переброшенной" из колоды в подмножество карты установил, что ошибка возникает в методе SUBSET::fill() после внесения последнего э-та. Вроде размерности все соответствуют т.е. скажем выделяю место под массив указателей на 6 карт, дальше ровно 6 указателей из объекта колоды туда и отправляю, но ошибка не уходит... Я явно, что то напутал либо с new, либо с delete, но и так и так пробовал и в к-торах и деструкторах и в fill() шаманил, никак не могу это отловить или как то исправить! Подскажите, пожалуйста, где у меня ошибка в коде и заодно соориентируйте правильно ли я использую delete в классах DECK и SUBSET для освобождения памяти?
0
|
23.03.2022, 21:09 | |
Ответы с готовыми решениями:
16
Ошибка при заполнении динамического массива Ошибка при заполнении динамического массива Ошибка при заполнении динамического массива точек Ошибка с функцией Val при заполнении двумерного динамического массива Ошибка при компиляции двумерного динамического массива указателей |
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
||||||
24.03.2022, 13:46 [ТС] | 3 | |||||
Это поправил. Не знал, что их надо так "занулять" при выделении памяти, ибо мы же сразу в fill() в них копируем указатели из объекта deck т.е. они неопределёнными вроде не остаются.
А как можно обнулить указатель ПОСЛЕ его удаления? Это согласен, спасибо! Пропустил, что лучше объект в функцию передавать по ссылке! К сожалению та же ошибка Debug Assertion по-прежнему появляется при завершении программы т.е. объект sub нормально заполняется картами из deck, и deck и sub корректно выводятся на экран, но УВЫ, то ли при выходе из main, то ли в процессе освобождения какой то памяти выскакивает этот неприятный error! Добавлено через 14 минут UPDATE1 Оказывается, я с момента задания вопроса в процессе отладки закомментил строку в определении метода CARD* DECK::drawCrd(int i)
Так вот, когда строку раскоменчиваешь, ошибка исчезает, но если мы её оставляем не работающей (т.е. указатели на карты уже скопировали в подмножество, но сами указатели на эти же карты исходной колоды оставляем без изменения, как будто колода не изменилась), этот баг опять начинает выскакивать. Т.е. при исключении карт из колоды всё ОК и в колоде и в подмножестве. А при копировании в подмножество с оставлением в колоде Debug Assertion Failure. Логики вообще не понимаю.
0
|
|
|
24.03.2022, 13:59 | 4 |
Сообщение было отмечено realalexandro как решение
Решение
два указателя на одну область памяти и два деструктора (дэк и сабсет) по очереди делают delete этих указателей
1
|
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
||||||
24.03.2022, 14:17 [ТС] | 5 | |||||
Ага! Так что нужно убрать один деструктор, например из сабсета, в случае, если решаем оставить "вытянутые" карты в колоде? Или как это исправить? Мне для понимания, поскольку путаюсь в теме освобождения памяти.
Добавлено через 12 минут Ну да, мой косяк! Получается, поскольку мы в Deck и так в конце программы все указатели на карты удаляем через,
Правда непонятно почему, если мы ту часть указателей, которые из Deck присвоили элементам Subset предварительно в Deck обнулили, то тогда деструктор Subset, который работает после или одновременно с дестр. Deck эту ошибку уже не вызывает, ведь мы всё равно пытаемся второй раз сделать delete тех областей памяти, которые уже освободили первым деструктором Deck?
0
|
фрилансер
5863 / 5402 / 1106
Регистрация: 11.10.2019
Сообщений: 14,435
|
|
24.03.2022, 15:40 | 6 |
realalexandro,
std::unique_ptr<CARD> в помощь Добавлено через 50 секунд и std::vector
0
|
90 / 60 / 32
Регистрация: 06.08.2020
Сообщений: 219
|
|
24.03.2022, 15:57 | 7 |
Нюанс в том, что delete не удаляет указатель, а только освобождает память. Полезно после делета его занулять. К тому же это помогает потом в деструкторе делать проверку указателя на нуль перед делетом. Благодаря этому не возникает ситуации с попыткой двойного делета уже освобожденной памяти.
1
|
|
|
24.03.2022, 16:05 | 8 |
0
|
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
|
24.03.2022, 19:09 [ТС] | 9 |
Так это где мне его надо обнулить получается после удаления - и в объекте DECK и в объекте SUBSET?
0
|
90 / 60 / 32
Регистрация: 06.08.2020
Сообщений: 219
|
||||||
25.03.2022, 13:46 | 10 | |||||
realalexandro, drwCrd я бы написал вообще по другому. тут ведь надо и указатель очистить, и что-то вернуть. Уж лучше тогда ничего не возвращать. Хотя возможны варианты.
Потом эти goto, первый раз их увидел в коде Не понял зачем в subset двойной указатель, но я вообще слабо в них ориентируюсь. В общем, немного переписал код на свой вкус:
1
|
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
|||||||||||
25.03.2022, 15:03 [ТС] | 11 | ||||||||||
PencilTorch, Спасибо за труд во-первых! Правда, "идеология" кода изменилась до неузнаваемости, но это, как говорится: "... как художник художнику..."
1. По поводу наследования. Класс SUBSET как раз задумывался как базовый, так что оно будет. Двойной указатель Card** subset был нужен затем, что это указатель на массив УКАЗАТЕЛЕЙ, каждый из которых в свою очередь указывает на конкретную карту (А НЕ ПРОСТО УКАЗАТЕЛЬ НА МАССИВ ГОТОВЫХ КАРТ!) т.е. это примерно то же самое, что и массив указателей на карты CARD* deck[52] в классе DECK в моём коде. Просто т.к. в колоде всегда 52 карты гораздо нагляднее так объявлять, что будет 52 указателя, чем просто объявлять указатель на массив карт CARD* deck = nullptr; и вводить переменную размерность unsigned short SIZE;, которая по сути не меняется. Я решил делать колоду именно указателями на карты, а не самими объектами карт, чтобы было быстрее их мешать (т.к. не понадобится копирование целых объектов при перестановках) т.е. в моей парадигме и в колоде и в SUBSET все действия должны происходить не с самими картами, которые раз и навсегда созданы в памяти(до срабатывания дест-ра), а с указателями на них т.е. с их адресами... 2. По поводу goto, его разумеется лучше вообще не использовать, но в данном случае т.к. это касается только локальной проверки того, что случайно генерируемые номера индексов массива CARD* deck[52] при перемешивании не должны совпадать, а если они случайно совпали, то сразу генерится новая пара, думаю, нормально. 3. CARD* DECK::drawCrd(int i) сначала копирует по индексу указатель во временную переменную, потом только зануляет соответствующий указатель в DECK и возвращает тот временный сохр. указатель т.е. мы получаем на выходе адрес карты, которую "вытащили" из колоды, он попадает в SUBSET, и одновременно из массива DECK мы его убираем т.к. одна и та же карта не может быть и на руках и оставаться в колоде, если это не шулер-казино 4. Проблему избежания "вторичного" удаления в дест-ре SUBSET тех объектов карт, которые уже могли быть удалены в рамках дестр-ра класса DECK (или наоборот), я решил путём добавления в каждую карту int поля count_ptrs в котором считается, сколько указателей в данный момент указывают на данную карту. Удаление в дест-ре каждой карты будет происходить только, когда остаётся только один указатель на данный конкретный объект. В противном случае просто уменьшаю счётчик числа указателей в карте в дестр-ре на 1 до тех пор пока от карты не "отвалятся" все указатели кроме одного. Когда же заводим карту через new и ставим на неё указатель счётчик указателей внутри карты ставим на 1 и потом увеличиваем на 1 каждый раз при копировании указателя на ту же карту в ещё одну переменную-указатель. При обнулении указателя уменьшаем соответственно. Для этого правда пришлось сделать классы DECK и SUBSET friend-ами по отношению к классу CARD. Хотя это нужно только на случай, если при вытаскивании карты из DECK в SUBSET (или из любого объекта некого класса в объект другого класса с указателями на карты) в колоде её оставляем т.е. не зануляем указатель и появляются множественные указатели на одну и ту же карту... Я на самом деле чего хотел понять и чего так до конца и не понял! Вот у нас есть, допустим массив УКАЗАТЕЛЕЙ на карты (а не самих карт!) т.е. массив адресов в памяти, например CARD* DECK[52] или CARD* SUBSET[size] с произвольным размером в данном случае не важно. Как НАДО подобный массив грамотно удалять в дест-ре: а.
б.
Вот что я хотел спросить.
0
|
90 / 60 / 32
Регистрация: 06.08.2020
Сообщений: 219
|
|
25.03.2022, 15:48 | 13 |
Ну идея вроде ясна, но в вашем коде в drawCrd создается временный указатель в который копируется адрес где лежит карта. Потом этот адрес зануляется и в итоге возвращается указатель на зануленный адрес.
0
|
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
|
25.03.2022, 16:40 [ТС] | 14 |
Нет, почему? Сначала адрес сохраняется в локальной переменной функции, т.е. мы чётко запомнили по какому адресу лежит объект карты, этот адрес мы и вернём через возврат по значению локальной переменной!
Только потом "исходник" этого адреса массиве deck[] зануляется, так что ничего не пропадает. Мы же возвращаем сохранённый адрес определённого участка в памяти (то же самое что указатель на некоторую область). А второй указатель при этом перестаёт указывать на эту область т.е. адрес который в нём хранился становится nullptr. Всё прекрасно работает!
0
|
фрилансер
5863 / 5402 / 1106
Регистрация: 11.10.2019
Сообщений: 14,435
|
||||||
25.03.2022, 16:42 | 15 | |||||
realalexandro, PencilTorch, вот, слегка упростил и приукрасил. В логику не вникал
1
|
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
|
||||||
25.03.2022, 16:49 [ТС] | 16 | |||||
Тогда получается надо так, если я правильно понял?:
Добавлено через 6 минут Алексей1153, Спасибо, будем учиться! Но я себе задачу поставил сделать без Vector-ов, просто с массивами и указателями т.е. по-колхозному))
0
|
фрилансер
5863 / 5402 / 1106
Регистрация: 11.10.2019
Сообщений: 14,435
|
|
25.03.2022, 16:58 | 17 |
0
|
25.03.2022, 16:58 | |
25.03.2022, 16:58 | |
Помогаю со студенческими работами здесь
17
Ошибка при создании динамического массива Ошибка при создании динамического массива Ошибка при создании динамического массива. Ошибка при создании динамического двумерного массива Ошибка при создании двумерного динамического массива Access Violation при повторном заполнении динамического массива Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи | |||||
[Golang] 121. Best Time to Buy and Sell Stock
alhaos 28.01.2025
В этой задаче мы получаем слайс целых чисел, которые означают цену акции в разные моменты времени, и должны вернуть максимально возможную прибыль от купли продажи акции.
/ / . . .
|
Проектирование и моделирование
hw_wired 28.01.2025
Введение в моделирование
Моделирование представляет собой один из фундаментальных методов научного познания, который позволяет изучать объекты и явления через создание их упрощенных аналогов. В. . .
|
Алгоритмы и исполнители
hw_wired 28.01.2025
Введение в алгоритмы
В современном мире информационных технологий алгоритмы играют основополагающую роль в решении различных задач и автоматизации процессов. Алгоритм представляет собой точную. . .
|
Хранение информации
hw_wired 28.01.2025
Введение: Роль систем хранения информации в современном мире
В современную эпоху цифровых технологий эффективное хранение информации становится одним из ключевых факторов успешного развития любой. . .
|
Обработка числовой информации
hw_wired 28.01.2025
Введение в обработку числовой информации
В современном мире обработка числовой информации стала неотъемлемой частью как профессиональной деятельности, так и повседневной жизни. Электронные таблицы. . .
|
Мультимедиа
hw_wired 28.01.2025
Введение в мультимедийные технологии
В современном мире мультимедийные технологии стали неотъемлемой частью нашей жизни, проникнув во все сферы человеческой деятельности. Термин "мультимедиа". . .
|
Обработка текстовой информации
hw_wired 28.01.2025
Введение в обработку текстовой информации
В современном мире обработка текстовой информации играет фундаментальную роль в различных сферах человеческой деятельности. Текстовые редакторы стали. . .
|
Обработка графической информации
hw_wired 28.01.2025
Введение в компьютерную графику
Компьютерная графика стала неотъемлемой частью современного цифрового мира, пройдя впечатляющий путь развития от простейших черно-белых изображений до сложных. . .
|
Python в Алгоритмике: Решение задач
hw_wired 28.01.2025
Введение в Python и Алгоритмику
В современном мире программирование стало неотъемлемой частью образования и профессионального развития. Python зарекомендовал себя как один из самых популярных и. . .
|
Компьютер как универсальное устройство для работы с информацией
hw_wired 28.01.2025
Введение в устройство компьютера
Компьютер представляет собой универсальное электронное устройство, предназначенное для автоматической обработки информации. В современном мире компьютер стал. . .
|
Информация и информационные процессы
hw_wired 28.01.2025
Понятие информации и ее виды
В современном мире информация является одним из фундаментальных понятий, пронизывающих все сферы человеческой деятельности. Под информацией понимают любые сведения об. . .
|
Алгоритмика
hw_wired 28.01.2025
Введение: Основы алгоритмики и её роль в информатике
В современном мире программирование и алгоритмическое мышление стали неотъемлемой частью образования и профессиональной деятельности. . . .
|