Защо промяната на начина, по който декларирах указатели към обекти, причинява толкова много грешки в сегментирането? C++

Въпрос:

Защо продължавам да получавам грешки при сегментиране, след като промених начина, по който декларирах и използвах моите указатели към обекти?

Според мен те изглеждат случайни, но съм сигурен, че има причина, обясняваща този хаос.

Брифинг:

Работя върху игра, подобна на Rogue, много подобна на NetHack, от няколко месеца и стигнах до друга точка, в която бях заседнал с дни. Подземието се показва и Играчът може да се движи наоколо с помощта на w,a,s,d. Когато влезете в контакт с същество, вие започвате да го атакувате. Имах това да работи до момента, в който Creature ‹= 0. Проблемът ми беше, че не можех да накарам показалеца на това създание да се върне към празно **Creature/NULL. Казаха ми, че това е свързано с начина, по който декларирах указателите в main.cpp, така че опитах няколко предложения и сега постоянно получавам seg faults. По някаква причина не мога да мръдна дори веднъж, след като играя през повечето време. Когато мога да се движа, съм добър, докато HP на Creature ‹= 0.. тогава seg fault срива програмата. Трябва да е мястото, където Създанието умира, но прекарах толкова много време в опити да го отстраня, а програмите за отстраняване на грешки отказваха да работят. Кълна се, че прекарах 10-15 часа с отстраняване на грешки и никой не ми позволи да стартирам програмата си!


Имам базов клас, Entity, който има 2 производни класа. Тези два производни класа са Предмет и Създание. Тези два подкласа трябва да са обясними.

  • Очевидно Item има няколко производни класа, които добавят повече специфики за типа Item. Като например Броня, Храна, Оръжие и т.н.

  • Creature очевидно има неща, които включват реално същество. С това казано, Creature има производен клас, Player, тъй като има само още няколко неща, които Player може да прави.

Някои неща за отбелязване:

  • Използвам Factory method pattern, който връща указатели за 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();

Първият начин ми позволи да направя всичко според очакванията, докато Creature трябва да бъде премахнато от нивото. Промених го на втория начин, защото хората ми казаха, че използвам указателите погрешно.

Код:

Ще добавя част от кода, където мисля, че може да възникнат проблеми. Въпреки това, публикувах този въпрос два пъти и всички ме укориха, че предоставям твърде много код и след това недостатъчен код.. надявам се, че ще задоволя всички и ще направя това по-лесно от това колко непосилно го направих да звучи, ще публикувам там, където < strong>Creature умира тук. Качих всичко в Cloud 9, така че всеки да може да види файловете ми в текущото им състояние и да стартира програмата, за да види къде има грешка.

Класовете, които използват тези указатели и биха могли да причинят грешките, които според мен са:

  • main.cpp
  • Създание
  • Плейър
  • Плочка
  • DungeonLevel

Опитвам се да не звуча така, сякаш искам някой да ми „даде отговора“, но последните два пъти, когато зададох по същество едно и също търсене, всички ме обвиняват в това. Опитах отстраняване на грешки и никой от програмите за отстраняване на грешки на моя Mac няма да се актуализира. Прекарах часове онази вечер, опитвайки това. Можете да видите тук: Защо стандартната библиотека на c++ не работи?

Това наистина не би трябвало да е толкова сложен проблем. Заседвам в даден момент и нищо, което правя, не намалява грешките :(

Всички мисли, съвети и предложения ще бъдат високо оценени.

Ето връзката към моя проект за облак 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)


Първо, важно е да разберете какво представлява грешката на сегментиране и какво я причинява. Не се опитвам да ви обвиня, че не знаете, но ако не знаете, страницата в wikipedia има доста прилична информация:

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

Има много различни начини за предизвикване на segfault, но най-често срещаният (по мое мнение така или иначе... най-големият ми източник на segfault, трябва да кажа) начин е чрез дерефериране на нулев указател (или изтрит/освободен указател).

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

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