Как проверить, была ли освобождена память в Destructor?

У меня есть простая игра в стиле танковых войн, использующая библиотеку с открытым исходным кодом allegro. В моем классе резервуара я инициализирую массивы указателей на растровые объекты равными 0. Затем я создаю новые объекты с помощью функции allegro create_bitmap, которая выделяет память и инициализирует ее.

Затем я занимаюсь своими делами, как обычно.

Проблема в том, что когда я собираюсь освободить растровую память в деструкторе класса, как хороший ООП-мальчик, я вылетаю из программы, потому что в этой конкретной программе библиотека allegro выполняет очистку (которая освобождает созданные ею растровые объекты) перед классом. выходит за рамки и уничтожается. Однако он не устанавливает мои указатели в NULL снова, поэтому я не могу проверить, действительны ли растровые изображения, и если я попытаюсь их освободить, это приведет к сбою программы.

Есть ли способ обойти это? Могу ли я проверить действительные указатели, если они не равны NULL? Как я могу быть УВЕРЕН, что память освобождается, если класс используется в программе по-другому. В нынешнем виде я, по сути, вызываю новый без удаления, и мне это не нравится.


person CodeFusionMobile    schedule 02.11.2009    source источник
comment
Кстати, Allegro дает команду destroy_bitmap для удаления ресурсов — почему бы тогда не вызвать destroy_bitmap в вашем dtor?   -  person Bill    schedule 02.11.2009
comment
Я пытался отредактировать OP, чтобы сказать, что я ЗНАЮ ЭТО. Это то, что я использую в dtor. Проблема в том, что если allegro уже освободил растровые изображения, когда я передаю освобожденный указатель команде destroy_bitmap, происходит сбой с ошибкой недопустимого доступа, потому что память не выделена, и allegro, по-видимому, не отслеживает это.   -  person CodeFusionMobile    schedule 02.11.2009
comment
Документация allgro по адресу alleg.sourceforge.net/stabledocs/en/alleg009.html# create_bitmap указывает, что память, выделенная с помощью create_bitmap, является вашей ответственностью за освобождение (с помощью destroy_bitmap).   -  person Bill    schedule 02.11.2009
comment
@Bill И я пытаюсь освободить его, но аллегро не позволяет мне, потому что думает, что я забыл, и делает это сам.   -  person CodeFusionMobile    schedule 02.11.2009


Ответы (9)


Я думаю, проблема не в том, что allegro сама выпускает растровые изображения (иначе вам не нужно было бы выпускать их при выходе), а в том, что библиотека allegro была деинициализирована до вызова деструктора.

int main()
{
    ObjectManagingBitmaps o;
    ...
    return 0;
    //allegro automatically shut down here
} //o destructor invoked here
END_OF_MAIN()

Чтобы убедиться, что деструктор вызывается первым, вы можете использовать искусственную область видимости:

int main()
{
    {
    ObjectManagingBitmaps o;
    ...
    } //o destructor invoked here
    return 0;
    //allegro automatically shut down here
} 
END_OF_MAIN()
person UncleBens    schedule 02.11.2009
comment
Является ли его объект даже основным? Бьюсь об заклад, его объект является глобальным. - person jmucchiello; 03.11.2009
comment
Это было бы довольно некрасиво. В таком случае глобальный может быть указателем, который удаляется до завершения main, или вы можете предоставить метод освобождения (и, возможно, проверить наличие NULL в деструкторе). Или... просто позвольте ОС очистить выделения, если этот объект предназначен для использования в качестве одного глобального/синглтона. - person UncleBens; 03.11.2009
comment
Мой объект является локальным для основного. Перед возвратом allegro требует вызова allegro_exit(), который деинициализирует библиотеку. Затем объект выходит из области видимости, когда main возвращается. Все, что вы сказали, верно. - person CodeFusionMobile; 03.11.2009

Вы не должны использовать массивы необработанных указателей. Allegro поставляется с функциями create_bitmap и destroy_bitmap. Это очень хорошо согласуется с концепцией конструкторов и деструкторов C++. У вас должен быть класс AllegroPlusPlus::bitmap, который управляет ровно одним растровым изображением. Ваш класс Tank может просто иметь их массив.

Это разделение обязанностей. Танковый класс не должен слишком много знать о растровых изображениях и управлении их памятью, а класс растровых изображений должен обрабатывать только одно растровое изображение.

Вы хотите переработать растровые изображения в своем классе Tank. Это не проблема; это можно легко сделать с хорошей реализацией bitmap::operator=(bitmap const&) или других перегрузок. Но опять же, возложите ответственность за это назначение на класс растровых изображений, а не на класс резервуара.

person MSalters    schedule 02.11.2009
comment
SO не отвечает, когда я пытаюсь отредактировать сообщение, поэтому я помещу его здесь. Это домашнее задание, и такое добавление классов за пределы заданных параметров нехорошо. - person CodeFusionMobile; 02.11.2009
comment
Добавление классов в класс С++ не разрешено? ВТФ? Если вы не можете создать новый класс как часть задания, откуда у вас проблемы? - person jmucchiello; 03.11.2009
comment
@jmucchiello Класс, с которым у меня возникла проблема с dtor, мне было приказано создать. В предыдущем задании меня высмеяли за то, что я использовал темы, слишком сложные для класса, в котором я учусь. - person CodeFusionMobile; 03.11.2009

Могу ли я проверить действительные указатели, если они не равны NULL?

Нет. Но в вашем случае и не нужно. Поскольку Allegro обещает позаботиться о своих ресурсах, вам не нужно (и не следует) вмешиваться в обработку ресурсов Allegro. В частности, поскольку вы даже не знаете, как распределяются ресурсы, вы не можете освободить их.

person Konrad Rudolph    schedule 02.11.2009
comment
Allegro обещает сделать это в самом конце программы, но если я повторно использую и удаляю этот объект танка несколько раз в игре, каждый раз растровые изображения не будут освобождаться должным образом. Кстати, Allegro дает команду destroy_bitmap для удаления ресурсов. - person CodeFusionMobile; 02.11.2009
comment
Кажется, это указывает на то, что ваш объект статически выделен. Поскольку порядок строительства (и разрушения!) статичных объектов не указан, постарайтесь не допустить такой ситуации. Если ваш объект не является статическим, ресурсы Allegro, следовательно, не должны удаляться в конце области видимости, и все в порядке. Тогда я не вижу проблемы. - person Konrad Rudolph; 02.11.2009

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

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

И нет, за исключением некоторых трюков с распределителем памяти, у вас нет стандартного способа определить, действительна память или нет. Хитрость распределителя может быть полезна для целей отладки, но не вмешивайтесь во внутренние компоненты библиотеки.

person Pontus Gagge    schedule 02.11.2009
comment
Что, если я просто установлю все свои указатели в NULL в dtor? Тогда я сделал свою часть, и это на аллегро, если они не очищены? - person CodeFusionMobile; 02.11.2009
comment
Да, это также хорошая практика, по крайней мере, в коде отладки, чтобы раньше инициировать ошибки из-за недопустимых указателей на держатель. - person Pontus Gagge; 02.11.2009
comment
Само по себе это неплохо, но тратит впустую один-два цикла процессора, если ваша программа корректна... Это зависит от вашей философии по отношению к обычному значению защиты (en.wikipedia.org/wiki/Defensive_programming) vs fail fast (en.wikipedia.org/wiki/Fail-fast). В режиме отладки я предпочитаю быструю ошибку; в производственном коде я предпочитаю отказоустойчивость (в зависимости от того, насколько сильно система может испортить ситуацию позже, если она устойчива прямо сейчас). - person Pontus Gagge; 03.11.2009

Звучит как довольно ужасная дырявая абстракция

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

Конечно, вы можете обернуть эти фрагменты и включить некоторую документацию в комментарии, чтобы другие разработчики не попали в ту же ловушку.

Также профилируйте свое приложение, чтобы убедиться в отсутствии утечек.

person Ali Parr    schedule 02.11.2009

Когда вызывается деструктор? Это после закрытия библиотеки Allegro? Если да, то можете ли вы сначала удалить все объекты?

person Matt Breckon    schedule 02.11.2009

Вы уверены, что используете это правильно? Я откопал часть своего старого кода Allegro, и у меня есть конструктор с вызовом create_bitmap и деструктор с вызовом release_bitmap, и он работал нормально.

Я ничего не помню об автоматическом освобождении памяти Allegro для вас. Вы случайно перезаписываете указатель каким-то значением, не связанным с памятью? Есть ли другое место, где этот указатель освобождается?

person Matt Breckon    schedule 02.11.2009
comment
Есть, я добавил столько же в ОП. destroy_bitmap(BITMAP*) — это то, что я пытаюсь использовать в файле dtor. Кроме того, как я уже говорил, Allegro пытается сделать это автоматически еще до того, как будет вызван dtor, что является корнем моей проблемы. - person CodeFusionMobile; 02.11.2009
comment
Исправление, я пытался добавить его в OP, SO почему-то не отвечает на мои правки. - person CodeFusionMobile; 02.11.2009
comment
Вы уверены, что Allegro освобождает память? Вы случайно перезаписываете указатель или освобождаете растровое изображение в другом месте? - person Matt Breckon; 02.11.2009
comment
@Matt B, нет места, где растровые изображения переназначаются. До вызова Allegro_exit() они в порядке, а после их нет. - person CodeFusionMobile; 03.11.2009

Используйте подсчет ссылок при работе с несколькими указателями на память в куче. По сути, если у вас есть несколько ссылок на одну и ту же память и вы удалите одну, другая ссылка может по-прежнему считать, что она существует.

http://en.wikipedia.org/wiki/Reference_counting

person Rantaak    schedule 02.11.2009

Я бы сказал, что вы должны убедиться, что все ваши объекты уничтожены, прежде чем закрывать Allegro, вы можете сделать это достаточно легко, (если они находятся в стеке), закрыв область, в которой они существуют, перед закрытием Allegro.

Если вам нужно закрыть Allegro раньше (например, из-за фатальной ошибки), вы можете просто вызвать выход, и в этом случае деструкторы не запустятся (но ваша программа все равно не рухнет).

Не тратьте слишком много времени на то, чтобы программа очищалась при выходе, сэкономьте свои усилия на том, чтобы убедиться, что она не протекает во время работы :)

person MarkR    schedule 13.11.2009