0xC0000005: Ошибка чтения местоположения нарушения прав доступа в деструкторе

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

Итак, мой класс (одноэлементный) спроектирован так:

class ImageManager
{
public:
   static ImageManager &getInstance();
   ImageManager(ImageManager const&) = delete;
   void operator=(ImageManager const&) = delete;
   void loagImage(char *location);
   ~ImageManager();
private:
   ImageManager();
   ALLEGRO_BITMAP *image = nullptr;
}

На самом деле в конструкторе нет ничего особенного. Просто загружает надстройку, связанную с обработкой растровых изображений. Необработанные указатели не создаются.

loadImage() реализовано, как показано ниже:

void ImageManager::loadImage(char *location)
{
    if(!location)
    {
       throw std::invalid_argument("Location cannot be null.");
    }
    image = al_load_bitmap(location);
}

Деструктор определяется как

ImageManager::~ImageManager()
{
   if(image)
   {
       al_destroy_bitmap(image); // Here I get the access violation exception.
   }
}

То, как этот класс используется в main.cpp, выглядит так:

int main(int argc, char *args[])
{
    ImageManager &imgManager = ImageManager::getInstance();
    imgManager.loadImage("valid/location");

    return 0;
}

Если я вызову al_destroy_bitmap() в той же функции, которая загружает растровое изображение, ошибки не будет. Это происходит только тогда, когда я пытаюсь вызвать его в деструкторе.

Я на Windows 10, используя VS17. Я видел ряд вопросов по той же теме, но я не мог понять ошибку, используя ответы там. Я также дам ссылку на два метода аллегро здесь, если вам нужно:

  1. al_load_bitmap()
  2. al_destroy_bitmap()

РЕДАКТИРОВАТЬ:

Мой метод getInstance():

ImageManager &ImageManager::getInstance()
{
    static ImageManager instance;
    return instance;
}

РЕДАКТИРОВАТЬ 2:

Точная ошибка 0xC0000005: Access violation reading location 0xDDDDDDF1.


person kovac    schedule 14.04.2019    source источник
comment
как выглядит ваш getInstance?   -  person AndersK    schedule 14.04.2019
comment
Что произойдет, если вы явно уничтожите экземпляр imgManager перед return 0? Битовая карта может быть уже уничтожена до вызова ImageManager::~ImageManager.   -  person J.R.    schedule 14.04.2019
comment
@Anders Добавлен метод getInstance().   -  person kovac    schedule 14.04.2019
comment
@Дж.Р. Пробовал явно вызывать деструктор, та же ошибка... Я думаю, вы правы в том, что растровое изображение уже освобождено, просто не могу понять, где...   -  person kovac    schedule 14.04.2019
comment
Как вызвать деструктор? Другим тестом было бы установить image на nullptr в ImageManager::~ImageManager...   -  person J.R.    schedule 14.04.2019
comment
Изменение instance на указатель, создание getInstance экземпляра и добавление явного вызова 'destroyInstance` для удаления instance перед return 0 может решить проблему, поскольку вы знаете, что после выхода из main у вас больше нет последовательности уничтожения/освобождения, которая находится вне вашего контроля.   -  person J.R.    schedule 14.04.2019
comment
@Дж.Р. Я сделал, как вы посоветовали, и теперь это работает. Не стесняйтесь публиковать ответ, чтобы я мог его принять.   -  person kovac    schedule 14.04.2019
comment
я думаю, что у вас есть другие проблемы в вашем коде, метод getInstance выглядит нормально, я бы посоветовал пропустить его как синглтон, так как синглтон может вызвать проблему, поскольку синглтоны большинство считают анти-шаблоном (хотя в некоторых особых случаях они вам нужны )   -  person AndersK    schedule 15.04.2019
comment
синглтоны похожи на глобальную переменную, поэтому у вас меньше контроля над ними   -  person AndersK    schedule 15.04.2019
comment
@ Андерс Спасибо, я с тобой согласен. Я намерен превратить его в обычный класс, хотя есть еще один класс, мне нужно сохранить синглтон (изменение этого просто приводит к другим сложностям). Так что совет здесь все же пригодится.   -  person kovac    schedule 16.04.2019


Ответы (2)


Allegro 5 плохо работает с глобальными переменными, такими как синглтоны.

То, что нужно запомнить :

1) Глобальные объекты или глобальные вызовы статической инициализации происходят до вызова al_init. Это означает, что вызовы аллегро-функций внутри их конструкторов завершатся ошибкой.

2) Глобальная статика переживет main и atexit. Это означает, что их деструкторы будут запускаться после завершения работы allegro. al_install_system перехватывает atexit для закрытия его библиотеки, если вы специально не запретите это делать. Это означает, что вызовы аллегро-функций, таких как al_destroy_bitmap, потерпят неудачу и segfault, если вам повезет.

Вам придется явно «выключить» свой экземпляр ImageManager перед выходом из atexit и main ИЛИ перед вызовом al_uninstall_system.

person BugSquasher    schedule 18.04.2019

Изменение instance на указатель, getInstance создание экземпляра и добавление явного вызова destroyInstance для удаления экземпляра перед return 0 может решить проблему, поскольку у вас больше нет последовательности уничтожения/освобождения, которая находится вне вашего контроля после выхода из main.

person J.R.    schedule 14.04.2019