Список STL копирует структуру, но скопированные значения смещены на два адреса памяти - PullRequest
0 голосов
/ 08 июля 2010

Я компилирую, используя Code :: Blocks в Windows 7, используя компилятор MinGW (я могу только предположить, что это последняя версия; Code :: Blocks и MinGW были установлены на прошлой неделе).Моя проблема возникает при определенных обстоятельствах, и мои попытки написать более простой сценарий, который демонстрирует проблему, потерпели неудачу (что подразумевает, что с моей структурой что-то не так).Кроме того, я прошу прощения за то, как долго этот пост.

В настоящее время я работаю с одним классом, FXSDL, который будет действовать как оболочка SDL:

class FXSDL
{
    public:
        FXSDL();
        virtual ~FXSDL();
        int Initialize();
        int Render();

        FXID CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims);
        int SetAnim(FXID hRefID, FXID hAnimID);
        FXID hPlayer;
    protected:
    private:
        list<FXSurface> m_lstFXObjects;
        list<FXSurface>::iterator m_liFirst;
        SDL_Surface* m_lpsfSDLScreen;
        Uint32 m_tmOld;
        Uint32 m_tmFrame;
};

Тип значенияМой список:

struct FXSurface
{
    FXID hRefID;
    int wpxTile;
    int hpxTile;
    int wpxTotal;
    int hpxTotal;
    int cntTiles;

    map<int, vector<int> > htAnims; // All animations
    map<int, vector<int> >::iterator vCurr; // Currently active animation
    vector<int>::iterator fiCurr; // Currently active frame
    SDL_Surface* lpsfSDL;
    SDL_Rect* lprcTiles;    // Predefined frame positions
    string* fpImage;
};

Я реализовал очень простую функцию инициализации и рендеринга.Функция CreateCharacter принимает несколько параметров, наиболее важным из которых является htAnims, карта целочисленных векторов (идея заключается в следующем: я определяю числовые идентификаторы с легко запоминаемыми представлениями, такими как FXA_IDLE или FXA_WALK, в качестве ключа, и сериючисловых значений, представляющих кадры для анимации как вектор).Это может быть довольно легко реализовано как многомерный целочисленный массив, но анимации имеют переменную длину, и я хочу иметь возможность добавлять новые анимации (или переопределять существующие) без необходимости переписывать массив.

Функция CreateCharacterэто просто.Он создает новый FXSurface, заполняет его необходимыми данными и помещает новый FXSurface в список:

FXID FXSDL::CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims)
{
    //list<FXSurface>::iterator lpsfTemp;
    FXSurface lpsfTemp;
    list<FXSurface>::iterator lpsfPos;
    SDL_Rect* lprcCurr = NULL;
    int cntTileW = 0;
    int cntTileH = 0;
    int cntCurr = 0;

    // Start off by initializing our container struct
    //lpsfTemp = new FXSurface();
    lpsfTemp.lpsfSDL = IMG_Load(fpImage.c_str()); // Try to load the requested image
    if(lpsfTemp.lpsfSDL != NULL) // If we didn't fail to
    {
        // Assign some variables for tracking
        lpsfTemp.hRefID = hRefID;
        lpsfTemp.fpImage = &fpImage;
        lpsfTemp.wpxTotal = lpsfTemp.lpsfSDL->w;
        lpsfTemp.hpxTotal = lpsfTemp.lpsfSDL->h;

        // If a tile width was specified, use it
        if(wpxTile != 0)
        {
            lpsfTemp.wpxTile = wpxTile;
            lpsfTemp.hpxTile = hpxTile;
        } // Otherwise, assume one tile
        else
        {
            lpsfTemp.wpxTile = lpsfTemp.wpxTotal;
            lpsfTemp.hpxTile = lpsfTemp.hpxTotal;
        }

        // Determine the tiles per row and column for later
        cntTileW = lpsfTemp.wpxTotal / lpsfTemp.wpxTile;
        cntTileH = lpsfTemp.hpxTotal / lpsfTemp.hpxTile;

        // And the total number of tiles
        lpsfTemp.cntTiles = cntTileW * cntTileH;
        lpsfTemp.lprcTiles = new SDL_Rect[cntTileW*cntTileH];

        // So we don't calculate this every time, determine each frame's coordinates and store them
        for(int h = 0; h < cntTileH; h++)
        {
            for(int w = 0; w < cntTileW; w++)
            {
                cntCurr = (h*cntTileW)+w;
                lprcCurr = new SDL_Rect;
                lprcCurr->w = lpsfTemp.wpxTile;
                lprcCurr->h = lpsfTemp.hpxTile;
                lprcCurr->x = w*lpsfTemp.wpxTile;
                lprcCurr->y = h*lpsfTemp.hpxTile;

                lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
                lprcCurr = NULL;
            }
        }

        // Now acquire our list of animations and set the default
        //lpsfTemp.htAnims = new map<int, vector<int> >(*htAnims);
        lpsfTemp.htAnims = htAnims;
        lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
        lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();

        this->m_lstFXObjects.push_back(lpsfTemp);
    }
    else
    {
        hRefID = 0;
    }

    return hRefID;
}

Именно при нажатии объекта происходит ошибка.Я прошел через код много раз.Изначально я мог только сказать, что мои итераторы не могли разыменовать объект FXSurface.После использования часов для определения точного адреса памяти, на который указали объекты итератора и списка, и разыменования адреса, я заметил причину моих ошибок: все значения, которые я поместил в исходный FXSurface, были сдвинуты вниз на два блока памяти, когда списокобъект скопировал его!

Мой процесс для этого прост.Я установил точку останова в операторе возврата для CreateCharacter, который дает мне представление о lpsfTemp (FXSurface, который я позже добавлю в список) и m_lstFXObjects (список, к которому я добавляю его).Я прокручиваю элементы m_lstFXObjects, что приводит меня к _M_node, который содержит адрес памяти единственного объекта, который я добавил до сих пор.Я добавляю часы по этому адресу в виде (FXSurface ) - шестнадцатеричный адрес здесь -

Сначала найдите адрес:

(здесь должна быть картинкапоказывает выделенный атрибут _M_node, содержащий адрес элемента списка, но я не могу публиковать изображения, и я могу публиковать только один URL. Второй гораздо важнее. Он расположен по адресу http://www.fauxsoup.net/so/address.jpg)

Далее,мы приводим и преобразуем адрес. Это изображение показывает и lpsfTemp, и копию в m_lstFXObjects; обратите внимание на несоответствие?

http://www.fauxsoup.net/so/dereferenced.jpg - Видите? Все значения в правильном порядке, просто смещены на двасписки

Первоначально я хранил fpImages как char *, поэтому я подумал, что это могло сбить с толку, но теперь это всего лишь указатель, и проблема сохраняется. Возможно, это связано с картойЯ храню?

Ответы [ 3 ]

4 голосов
/ 08 июля 2010

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

Я не собираюсь смотреть дальше.

Используйте умные указатели для управления ресурсами. Не помещайте «голый» ресурс в тип, кроме случаев, когда единственное намерение этого типа - управлять этим одним ресурсом. От другого ответа , полученного вчера:

Как правило: Если вам нужно вручную управлять ресурсами, оборачивает каждый в свой собственный объект.

1 голос
/ 08 июля 2010

На первый взгляд, я бы сказал, что вы дважды удаляете lpsfSDL и / или lprcTiles. Если в вашей структуре есть необработанные указатели, вам нужно следовать правилу трех (реализовать конструктор копирования, оператор присваивания и деструктор) для правильного управления памятью.

0 голосов
/ 08 июля 2010

Эти строки выглядят неправильно для меня:

            lprcCurr = new SDL_Rect;
            lprcCurr->w = lpsfTemp.wpxTile;
            lprcCurr->h = lpsfTemp.hpxTile;
            lprcCurr->x = w*lpsfTemp.wpxTile;
            lprcCurr->y = h*lpsfTemp.hpxTile;

            lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
            lprcCurr = NULL;

lpsfTemp.lprcTiles - это SDL_Rect*. lprcTemp.lprcTiles[cntCurr] является SDL_Rect. Вы должны написать это, ИМХО:

            SDL_Rect tmpRect;
            tmpRect.w = lpsfTemp.wpxTile;
            tmpRect.h = lpsfTemp.hpxTile;
            tmpRect.x = w*lpsfTemp.wpxTile;
            tmpRect.y = h*lpsfTemp.hpxTile;

            lpsfTemp.lprcTiles[cntCurr] = tmpRect;

Дамп lprcCurr целиком.

Теперь этот код:

    lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
    lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();

Это плохо. Эти итераторы становятся недействительными, как только завершается push_back. Этот push_back делает копию из lpsfTemp. Члены карты и вектора будут копировать себя, и эти итераторы будут копировать себя, но они будут указывать на элементы lpsfTemp, которые будут уничтожены, как только выйдет CreateCharacter.

Один из способов исправить это состоит в том, чтобы push_back объект FXSurface в начале, использовать back () для получения его ссылки и оперировать этим вместо lpsfTemp. Тогда итераторы останутся согласованными, и они должны остаться согласованными, поскольку вы используете список, который не копирует свои объекты. Если бы вы использовали вектор, deque или что-либо, кроме списка, вам нужно было бы управлять всеми этими указателями и итераторами в конструкторе копирования и операторе присваивания.

Еще одна вещь: дважды и трижды проверяйте границы вашего массива при обращении к этому массиву lprcTiles. Есть какая-то ошибка, и вы могли бы писать, кто что знает.

Я не знаю, поможет ли вам что-нибудь из этого.

...