Указатели C ++ и иерархия классов, вызывающая загадку сегфо

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

#include <iostream>
#include <string>
#include <vector>

class GameObject {
public:
virtual void update();
void addChild(GameObject* child);

GameObject* parent;
std::vector<GameObject*> children;
int x;
int y;
};

class Player : public GameObject {
public:
void growTail();
void update();
};

class TailNode : public GameObject {
public:
void addTo(GameObject* parent);
void update();
// UPDATE AFTER ANSWER IS CLEAR: this was in original codebase and it was causing the segfault
GameObject* parent;
};

void GameObject::addChild(GameObject* child) {
this->children.push_back(child);
child->parent = this; // <- can't do this in full codebase, causes segfault later
}

void GameObject::update() {
for (GameObject* child : children) {
child->update();
}
}

void Player::update() {
GameObject::update();
std::cout << "Player at: ["<< std::to_string(x)
<< std::to_string(y)
<< "]"<< std::endl;
}

void Player::growTail() {
TailNode* tail = new TailNode();
tail->addTo(this);
}

void TailNode::update() {
GameObject::update();
std::cout << "Tail parent at: ["<< std::to_string(parent->x) // <- if parent is set inside GameObject::addChild, this segfaults in full codebase
<< std::to_string(parent->y)
<< "]"<< std::endl;
}

void TailNode::addTo(GameObject* parent) {
parent->addChild(this);
// this->parent = parent; <-- have to do this to avoid segfault in full codebase
}

int main() {
Player* player = new Player();
player->x = 10;
player->y = 11;
player->growTail();
player->update();
}

Теперь, в самом исходном коде игры, он делает segfault, так как очевидно, что хвостовой узел получает плохого родителя.

Когда я двигаюсь child->parent = this; от GameObject::addChild и использовать this->parent = parent; в TailNode::addTo, оно работает.

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

Вы можете найти полную рабочую кодовую базу на https://github.com/spajus/sdl2-snake/tree/tail_working
И коммит, который нарушает это: https://github.com/spajus/sdl2-snake/commit/4e92e9d6823420ce7554f2b6d7d19992c48d4acc

Я компилирую и запускаю его на OS X 10.11.5,

Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.5.0
Thread model: posix

Код зависит от SDL2 и нескольких его расширений.

Пример команды компиляции:

$XCODE/XcodeDefault.xctoolchain/usr/bin/c++ \
-I/Library/Frameworks/SDL2.framework/Headers \
-I/Library/Frameworks/SDL2_image.framework/Headers \
-I/Library/Frameworks/SDL2_mixer.framework/Headers \
-I/Library/Frameworks/SDL2_ttf.framework/Headers \
-I$HOME/Dev/sdl2-snake/include \
-Wall -Wextra -pedantic -std=c++11 \
-Wall -Wextra -pedantic -std=c++11 \
-g -g -o CMakeFiles/Snake.dir/src/main.cpp.o \
-c $HOME/Dev/sdl2-snake/src/main.cpp

Я знаю, что вы порекомендуете использовать умные указатели вместо обнаженных, и вы можете быть правы, но здесь я просто развлекаюсь и мне интересно узнать, почему это так ломается. Это может быть трудно воспроизвести (хотя есть сценарии сборки), но, возможно, опытный разработчик C ++ с хорошим вниманием сразу же обнаружит проблему.

Кроме того, я ценю предложения по пересмотру кода и улучшению, мои навыки C ++ близки к нулю, так как я ничего не делал с тех пор, как в университете более десяти лет назад.

3

Решение

Благодаря совету orbitcowboy, я использовал cppcheck который сразу нашел проблему:

[include / snake / tail_node.hpp: 13] -> [include / snake / game_object.hpp: 18]: (предупреждение) Класс ‘TailNode’ определяет переменную-член с именем ‘parent’, также определенным в его родительском классе ‘GameObject’ ,

После удаления переопределения GameObject* parent от TailNode класс, это больше не segfaults.

Фиксация, которая решает проблему: https://github.com/spajus/sdl2-snake/commit/a1808e7d2426b5aedd9ab3a4dc2aa38aa0225a95

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]