2014-10-15 2 views
1

Я пишу игру код C++, чтобы узнать его и игру dev, обернув SDL по пути; все, что он делает прямо сейчас, это загрузка изображения BMP и отображение его на экране в цикле. Проводка только соответствующего кода.Указатель Invalidation с std :: vector

EDIT: Я пересматриваю это сообщение до MVCE. Проблема может быть найдена гораздо более непосредственно.

#include <SDL.h> 
#include <vector> 

class Surface { 
public: 
    Surface(SDL_Surface*); 
    ~Surface(); 
    SDL_Surface* mSurface; 
} 

Surface::Surface(SDL_Surface* surface) { 
    mSurface = surface; 
} 

Surface::~Surface() { 
    SDL_FreeSurface(mSurface); 
} 

int main(int argc, char* args[]) 
{ 
    if (SDL_Init(SDL_INIT_VIDEO) < 0) { 
     throw("Failed to SDL_Init: " + std::string(SDL_GetError())); 
    } 

    SDL_Window* window = SDL_CreateWindow("SDL TEST", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 960, SDL_WINDOW_SHOWN); 
    SDL_Surface* screenSurface = SDL_GetWindowSurface(window); 

    const char * imgPath = "test.bmp"; 
    std::vector<Surface> surfaces; 
    surfaces.push_back(Surface(SDL_LoadBMP(imgPath))); 
    SDL_BlitSurface(surfaces.begin()->mSurface, // memory access violation here 
          NULL, screenSurface, NULL); 
    SDL_UpdateWindowSurface(window); 
    SDL_Delay(2000); 

    SDL_Quit(); 
    return 0; 
} 

Что это сводится к тому, что указатель внутри Surface объекта, загруженного в vector становится недействительным. Я читал вокруг и подумал, что, возможно, это было связано с нуждаясь пользовательский конструктор копирования, но сделать один, как это не решить:

Surface::Surface(const Surface& other) { 
    mSurface = other.mSurface; 
    mIsWindowSurface = other.mIsWindowSurface; 
} 
+0

Если 'SDL_BlitSurface' отлично работает с жестким кодированным контуром, то вам, вероятно, стоит попробовать стрелять в' loadMedia'. Может быть, проверить возвращаемое значение 'SDL_LoadBmp' перед конструктором' Surface'? Есть ли какой-либо SDL API для получения последней ошибки? Я никогда не работал с SDL, чтобы предлагать такой API для поиска ошибок. – elimad

+0

@elimad, указатели все выглядят нормально в точке, в которой вызывается 'SDL_BlitSurface'. Я также использовал 'SDL_GetError', чтобы узнать, установлено ли что-либо перед blitting, но ничего не выходит. Существует некоторая недействительность между тем, когда 'Surface' создается в' loadMedia' и когда указатель из него используется в 'update'; но он становится «фиксированным», если эти две строки раскоментированы (даже не используются). – esel

+0

Как вы инициализируете 'mWindowSurface.pointer()' скорее, 'mWindowSurface'? – elimad

ответ

1

Вы не определили копию-конструктор, поэтому копию по умолчанию конструктор используется, и это не делает то, что вы хотите.

В строке

surfaces.push_back(Surface(SDL_LoadBMP(imgPath))); 

вот что происходит:

  1. Создание временных Surface которого mSurface указатели на вновь выделенной поверхности
  2. Скопируйте этот Surface в вектор с использованием интеллектуальной собственности; конструктор (или оператор присваивания копии)
  3. Уничтожить временный Surface, который вызывает SDL_FreeSurface.

Затем, когда вы переходите на do surfaces.begin()->mSurface,, вы пытаетесь работать с уже освобожденной поверхностью.

Чтобы исправить это до C++ 11, вам нужно будет сделать ваш экземпляр-конструктор и оператор присваивания копии семантически сделать копию или использовать механизм отсчета ссылок, чтобы гарантировать, что SDL_FreeSurface вызывается только тогда, когда нет более активные указатели на поверхность.

В C++ 11 есть намного больше исправлений из коробки; один из них - отключить копирование для вашего класса и реализовать оператор move-constructor и move-assign operator и/или использовать emplace_back(SDL_LoadBMP ... вместо push_back.

+0

Это имеет смысл! Я не думал о вызове деструктора. Сначала я попробовал просто заменить «emplace_back», но проблема все еще существует; есть идеи? Я сейчас пытаюсь создать конструктор/оператор перемещения. – esel

+1

убедитесь, что вы выполняете 'emplace_back (SDL_LoadBmp' ..., а не' emplace_back (Surface'). Отключите копирование для вашего класса, чтобы вы получили ошибку компилятора вместо случайной копии, если что-то пытается ее скопировать. –

+0

Это было Это два вопроса: 1) В моем случае я не могу использовать конструктор перемещения или копирования, потому что у старого объекта всегда будет свой вызов dtor, правильно? 2) Есть ли аргумент, который нужно сделать за или против выполнения emplace vs move, кроме того, «зависит от необходимости приложения»? то есть в моем случае, если бы я мог использовать это, было бы лучше, чем другое? – esel

Смежные вопросы