Почему изменение способа объявления указателей на объекты вызывает так много ошибок сегментации? С++

Вопрос:

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

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

Брифинг:

Я работал над Rogue-подобной игрой, очень похожей на NetHack, в течение нескольких месяцев и достиг еще одного момента, когда я застрял на несколько дней. Отображается подземелье, и игрок может перемещаться с помощью w,a,s,d. Когда вы вступаете в контакт с Существом, вы начинаете атаковать его. У меня это работало до момента, когда Creature ‹= 0. Моя проблема заключалась в том, что я не мог заставить указатель этого существа вернуться к пустому **Creature/NULL. Мне сказали, что это связано с тем, как я объявил указатели в main.cpp, поэтому я попробовал несколько предложений, и теперь я постоянно получаю ошибки seg. По какой-то причине я не могу даже пошевелиться после запуска игры большую часть времени. Когда я могу двигаться, я в порядке, пока HP Creature не станет ‹= 0.. затем ошибка seg приводит к сбою программы. Это должно быть место, где Существо умирает, но я потратил так много времени на его отладку, а отладчики отказывались работать. Клянусь, я потратил 10-15 часов на отладку, и ни одна не дала мне запустить мою программу!


У меня есть базовый класс Entity, который имеет 2 производных класса. Эти два производных класса — Предмет и Существо. Эти два подкласса должны говорить сами за себя.

  • Очевидно, что Item имеет несколько производных классов, которые добавляют больше деталей для типа Item. Например, Броня, Еда, Оружие и т. д.

  • Существо, очевидно, имеет вещи, которые связаны с живым существом. При этом у Creature есть производный класс, Player, поскольку Player может делать еще несколько вещей.

Некоторые вещи, на которые следует обратить внимание:

  • Я использую шаблон метода Factory, который возвращает указатели на Creatures, а другой — на Items. Сейчас я сосредоточен на Creature.

  • В main.cpp я создаю один объект Player. Затем я создаю 3 указателя на Creatures, где/когда начали происходить ошибки сегментации. Вот как он у меня был до того, как я его изменил, а затем как я его изменил.

    Creature * pCreature1 = NULL;
    Creature * pCreature2 = NULL;
    Creature * pCreature3 = NULL;
    

To:

    Creature * pCreature1 = new Creature();
    Creature * pCreature2 = new Creature();
    Creature * pCreature3 = new Creature();

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

Код:

Я добавлю часть кода, где, как мне кажется, могут возникнуть проблемы. Тем не менее, я публиковал этот вопрос еще два раза, и все ругали меня за то, что я предоставил слишком много кода, а затем недостаточно кода. strong>Существо умирает здесь. Я загрузил все в Cloud 9, чтобы каждый мог просмотреть мои файлы в их текущем состоянии и запустить программу, чтобы увидеть, где что-то не так.

Я считаю, что классы, которые используют эти указатели и могут вызывать ошибки:

  • main.cpp
  • Существо
  • Игрок
  • Плитка
  • Уровень подземелья

Я пытаюсь не звучать так, будто хочу, чтобы кто-то «дал мне ответ», но последние два раза я задавал практически один и тот же квест, и все обвиняют меня в этом. Я пробовал отладку, и ни один из отладчиков на моем Mac не обновляется. Я провел несколько часов прошлой ночью, пытаясь это сделать. Вы можете увидеть здесь: Почему стандартная библиотека С++ не работает?

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

Любые мысли, советы и предложения будут высоко оценены.

Вот ссылка на мой проект Cloud 9: http://c9.io/moddedlife/jhackpublic

Все остальное на этой странице будет фрагментами кода. Спасибо еще раз!

Метод атаки игрока:

void Player::attack(Creature * monster, Creature * player, std::mt19937 & randomGen,     DungeonLevel & dl){
    int monsterHit = monster->getHit(randomGen);
    int playerHit = getHit(randomGen);

    if ((monster->getHP() - playerHit) <= 0){
            playerHit = monster->getHP();
            cout << "Monster name: " << monster->getName() << endl;
            delete monster;
            monster = NULL;
            cout << "Monster name after: " << monster->getName() << endl;
    }
    else if ((player->getHP() - monsterHit) <= 0){
            monsterHit = player->getHP();
            //game over
    }

    cout << "You hit: " << playerHit << endl;
    player->setHP((player->getHP() - monsterHit));
    player->addXp(playerHit);

    if (monster != NULL){
            cout << "Monsters Hit: " << monsterHit << endl;
            monster->setHP((monster->getHP() - playerHit));
            cout << "Your HP: [" << player->getHP() << "]/[" << player->getMaxHP() << "]" << endl;
            cout << "Monsters HP: [" << monster->getHP() << "]/[" << monster->getMaxHP()     << "]" << end$
    }
    else {
            cout << "you Killed it!" << endl;
    }
    return;
}

Метод перемещения игрока:

Я собирался поместить весь этот код, но он, по сути, один и тот же - только с изменением направления... каждое направление составляет около 30-50 строк. Вы можете просмотреть весь код любого из моих файлов здесь: http://c9.io/moddedlife/jhackpublic< /а>

Я БУДУ добавлять код сюда, если использование облака 9 не разрешено или что-то в этом роде. Я просто подумал, что это будет намного проще, чем вставка блоков из нескольких файлов.

Спасибо


person JGeis    schedule 28.04.2013    source источник
comment
Как правило, указатели в C++ не используются. В частности, если у вас есть опыт работы с Java, вам нужно избавиться от привычки везде использовать new. Если вы используете указатели, вы также должны использовать интеллектуальные указатели, которые на самом деле являются классами-оболочками, предотвращающими несколько распространенных ошибок.   -  person Ulrich Eckhardt    schedule 28.04.2013


Ответы (2)


Прежде всего, важно понять, что такое ошибка сегментации и что ее вызывает. Я также не пытаюсь обвинить вас в том, что вы не знаете, но если вы этого не сделаете, на странице википедии есть достойная информация:

Ошибка сегментации... обычно представляет собой попытку доступа к памяти, к которой процессор не может физически обратиться. (http://en.wikipedia.org/wiki/Segmentation_fault)

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

Судя по беглому просмотру кода, который вы разместили, я вижу это:

delete monster;
monster = NULL;
cout << "Monster name after: " << monster->getName() << endl;

Помните, что как только вы удалите (или освободите) какой-либо выделенный блок памяти, вы НЕ должны пытаться получить к нему доступ. Вызывая monster->getName(), вы разыменовываете (теперь) NULL-указатель, что обычно приводит к очень нежной, но ужасной ошибке сегментации.

Я не могу обещать, что это ЕДИНСТВЕННАЯ оскорбительная строка (строки) (в конце концов, я просмотрел только несколько опубликованных вами фрагментов), но это должно быть началом, и, надеюсь, вы будете знать, что искать в своем коде.

person void ptr    schedule 28.04.2013
comment
Ооо ок. Спасибо! Я не осознавал, что сделал это. У меня было это изначально, чтобы проверить, действительно ли монстр был удален. - person JGeis; 28.04.2013

Это слишком длинная история, чтобы найти точную проблему, но delete бить монстра, если он мертв, кажется подозрительным. Я рекомендую две вещи: сделать «мертвым» состояние монстра и использовать умные указатели. вообще не нужно обрабатывать случай NULL или удаление.

person Elazar    schedule 28.04.2013
comment
да извини. Эту концепцию легко понять, но из-за размера файлов она требует подробного объяснения. Спасибо, я попробую это. - person JGeis; 28.04.2013