C ++ Destructor вызывается, когда я не ожидаю, что он будет - PullRequest
1 голос
/ 25 июня 2019

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

My debug output

Это в основном мой класс GameObject:

class GameObject
{
public:
int             xCoord = 0, yCoord = 0, prevX, prevY;
int             status = 0, weight = -1;
int             id = -1;

GameObject(CommandComponent* commands,
    PhysicsComponent* physics,
    GraphicsComponent* graphics)
    : commandComponent(commands),
    physicsComponent(physics),
    graphicsComponent(graphics)
{};

~GameObject()
{
    std::cout << "Destructor called " << id << std::endl;
    delete commandComponent;
    delete physicsComponent;
    delete graphicsComponent;
};

void update(World& world, int command, sf::Time dt)
{
    commandComponent->update(*this, world, command);
    physicsComponent->update(*this, world);
    graphicsComponent->update(*this, dt);
};

void update(World& world, int command)
{
    commandComponent->update(*this, world, command);
    physicsComponent->update(*this, world);
};

sf::Sprite draw()
{
    return *(graphicsComponent->draw());
};

void setCoords(int x, int y)
{
    prevX = xCoord;
    xCoord = x;
    prevY = yCoord;
    yCoord = y;
};

void setId(int newId)
{
    id = newId;
}


private:
    CommandComponent*           commandComponent    = NULL;
    GraphicsComponent*          graphicsComponent   = NULL;
    PhysicsComponent*           physicsComponent    = NULL;

};

Это метод createPlayer:

    GameObject* createPlayer(sf::Texture* text)
{
    return new GameObject(new PlayerCommandComponent(), new PlayerPhysicsComponent(), new PlayerGraphicsComponent(text));
};

Это метод, который я вызываю для добавления нового объекта в вектор на основе того, является ли он активным или неактивным объектом, я такжедобавьте его в массив:

void World::addObject(GameObject object, int id, int type){
object.setId(id);

if (type == 0)
{
    inactiveObjects.push_back(object);
}
else if (type == 1)
{
    activeObjects.push_back(object);
}
}

Наконец, это мой тестовый код, который создает игровые объекты и вызывает вышеуказанную функцию, и где я вижу вызываемые деструкторы:

void World::test()
{
// Player
std::cout << "Starting to create id 0\n";
addObject((*createPlayer(&(mTextures.get(Textures::PlayerCharacter)))), 0, 1);
activeObjects.at(0).setCoords(3, 3);
activeObjects.at(0).weight = 10;
std::cout << "Created id 0\n";

// Test Objects
std::cout << "Starting to create id 1\n";
addObject((*createPlayer(&(mTextures.get(Textures::PlayerCharacter)))), 1, 1);
activeObjects.at(1).setCoords(3, 4);
activeObjects.at(1).weight = 7;
std::cout << "Created id 1\n";

addObject((*createPlayer(&(mTextures.get(Textures::PlayerCharacter)))), 2, 1);
activeObjects.at(2).setCoords(5, 4);
activeObjects.at(2).weight = 2;

addObject((*createPlayer(&(mTextures.get(Textures::Enemy)))), 3, 1);
activeObjects.at(3).setCoords(6, 6);
activeObjects.at(3).weight = -1;

addObject((*createPlayer(&(mTextures.get(Textures::Enemy)))), 4, 1);
activeObjects.at(4).setCoords(1, 1);
activeObjects.at(4).weight = 0;

std::cout << "Done Creating Test Objects\n";

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

1 Ответ

2 голосов
/ 25 июня 2019

Несколько вещей здесь играют.

GameObject* createPlayer(sf::Texture* text)

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

addObject((*createPlayer(&(mTextures.get(Textures::PlayerCharacter)))), 0, 1);

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

Хранение разыменованного объекта вызовет либо конструктор копированияили оператор присваивания, и на этом этапе вам нужно рассмотреть Правило трех : если вам нужно определить собственный деструктор, вам, вероятно, нужно определить пользовательский оператор присваивания и конструктор копирования.Это стандартный пример того, когда вам нужно соблюдать Правило Трех .Что не так, хорошо объяснено в правиле трех Ссылка, поэтому остановитесь, прочитайте и поймите это, прежде чем идти дальше.Неспособность сделать это означает, что остальная часть этого ответа будет для вас почти бесполезной.

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

Вы можете обойти ПравилоТри здесь, изменив

void World::addObject(GameObject object, int id, int type)

на

void World::addObject(GameObject * object, int id, int type)

и передав object по ссылке.Это мало помогает, потому что

inactiveObjects.push_back(object);

ожидает объект, а не указатель.

Вы также можете изменить это, но не так ли?std::vector в лучшем случае, когда он непосредственно содержит объект.Указатели приводят к погоне за указателями, плохому поведению в кэшировании и, в конечном итоге, suuuhhhfering .Не храните указатели, если у вас нет веских причин для этого.

И если вы это сделаете, управляйте указателями с начала std::unique_ptr до конца.

Что бы я сделал:

Перепрыгните прямо через правило трех и перейдите к Правило пяти .

  1. Укажите как можно больше динамически распределенных переменных, чтобы мне не было необходимостипроделать большую работу, если она есть, с пунктом 2. Это означает, что указатели для (или для) commandComponent, physicsComponent и graphicsComponent, если это возможно, отсутствуют.
  2. Добавьте конструктор перемещения и оператор перемещения вGameObject, а также CommandComponent, PhysicsComponent и GraphicsComponent.Держите все управление ресурсами как можно ближе к ресурсу.Это позволяет вам сохранять классы более высокого уровня как можно более невежественными.Если GraphicsComponent знает, как копировать и перемещать себя, GameObject не нужно знать, как его перемещать.Это позволяет вам использовать в своих интересах Правило нуля , и Правило нуля должно быть тем, к чему вы стремитесь во всех своих классах.
  3. Используйте семантику перемещения, чтобы получить GameObject, а не GameObject* с createPlayer до activeObjects и inactiveObjects vector с.
  4. Наслаждайтесь сниженной нагрузкой на управление памятью.
...