указатели — нежелательный вызов деструктора происходит с выделенным классом стека

В функции класса Game я размещаю класс Boundary в стеке

void Game::loadContent()
{
Boundary b(this, body);
}

Класс границы имеет указатель на основной класс Game и указатель на твердое тело. Я не уверен, должен ли я использовать ссылку для каждого хотя? Некоторая ясность здесь будет полезна по причинам, объясненным позже.

class Boundary : public DynamicEntity
{
public:
Boundary(Game *game, btRigidBody *body);
~Boundary(void);

// Override functions
virtual void draw(float dt);
virtual glm::vec3 getPosition();
virtual void update(float dt);
};

Класс DynamicEntity назначает тело и обрабатывает удаление указателя в его деструкторе.

class DynamicEntity : public Entity
{
public:
virtual ~DynamicEntity(void);

virtual void draw(float dt) = 0;
btRigidBody* getBody();
glm::vec3 getPosition() = 0;
virtual void update(float dt) = 0;

protected:
explicit DynamicEntity(Game *game, btRigidBody *body);
btRigidBody *m_body;
};
DynamicEntity::~DynamicEntity(void)
{
m_game->m_dynamicsWorld->removeRigidBody(m_body);

delete m_body;
}

DynamicEntity происходит от базового класса всех игровых объектов, называемых Entity

class Entity
{
public:
// Make destructor virtual as this is a base class
virtual ~Entity(void);

virtual void draw(float dt) = 0;
int getID();
virtual glm::vec3 getPosition() = 0;
virtual void update(float dt) = 0;

protected:
explicit Entity(Game *game);                // Abstract base constructor

Game *m_game;
int m_id;                                   // Unique ID
};

Я не могу вызвать delete для указателя класса Game в деструкторе этого класса, хотя я не уверен, является ли передача в качестве указателя правильным методом (вместо ссылки)?

Entity::~Entity(void)
{
// Derived class destructors are called first
delete m_game;  // ERROR
}

Класс Entity добавляет к себе указатель, к которому можно получить доступ через список в классе Game (полезно для итерации и вызова функций Entity в основном классе Game).

Entity::Entity(Game *game)
: m_game(game),                                         // Initialise members
m_id(m_game->g_idGenerator->generateNewID())            // Generate unique ID
{
m_game->m_entities.push_back(std::shared_ptr<Entity>(this));
}

Основная проблема, с которой я сталкиваюсь, заключается в том, что Игра :: LoadContent () Метод завершен, деструктор вызывается для класса Entity. Это разрушает * shared_ptr *, хранящийся в списке, и возникают ошибки при попытке вызвать любой из виртуальных методов.

Я бы хотел, чтобы указатель границы сохранялся до тех пор, пока я не скажу удалить. Есть ли способ сделать это без выделения границы в кучу?

В ответ на предложение по использованию Const& Игра

Похоже, что я должен изменить свой заголовок сущности на следующий

#pragma once

#include <glm\glm\glm.hpp>

#include "Game.h"
// Forward declarations
class Game;

class Entity
{
public:
// Make destructor virtual as this is a base class
virtual ~Entity(void);

// '= 0' means pure virtual function (like 'abstract' in C#)
// This means they do not have to be declared in the source file '.cpp'
virtual void draw(float dt) = 0;
int getID();
virtual glm::vec3 getPosition() = 0;
virtual void update(float dt) = 0;

protected:
explicit Entity(const Game &game);          // Abstract base constructor

Game m_game;
int m_id;                                   // Unique ID
};

Не Game m_game выделить экземпляр класса Game в стек? Как это должно быть объявлено в заголовке, если оно представляет ссылку?

Если я храню защищенную ссылку на класс Game в базовом классе Entity const Game &m_game Я не могу получить доступ к глобальному члену класса Game g_wireShapeDrawer в производных классах.

class Game
{
public:
GL_WireShapeDrawer g_wireShapeDrawer;

Game(void);
~Game(void);

void init();
void draw(float dt);
void handleInput(float dt);
void loadContent();
void update(float dt);
};

Например, я получаю следующую ошибку при попытке доступа к глобальному члену в методе рисования источника производного класса Boundary

void Boundary::draw(float dt)
{
m_game.g_wireShapeDrawer.drawPlane(glm::vec3(0, 1, 0), 0.0f, glm::vec4(1, 1, 1, 1));
}

ошибка C2662: «GL_WireShapeDrawer :: drawPlane»: невозможно преобразовать этот «указатель» из «const GL_WireShapeDrawer» в «GL_WireShapeDrawer» &

Почему это?

1

Решение

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

Вы должны передать это своему Entity классы как Game&, Зачем? Потому что у вас есть только один экземпляр Game, поэтому нет необходимости сбрасывать то, на что он указывает, и это всегда должно быть действительным (так как игра будет существовать до Entity объекты делают).

Другой вариант заключается в реализации Синглтон в вашем Game класс, и доступ к нему, как это Game::GetInstance().m_dynamicsWorld->removeRigidBody(m_body);

Согласно вашему редактированию, вы можете создать Entity используя список инициализаторов. Таким образом, вы можете хранить постоянные члены, вот так:

class Entity
{
protected:
explicit Entity(Game &game) : m_game(game) {}
private:
Game& m_game;
}
2

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

Ваш дизайн имеет недостатки. Вы должны четко указать (через ваш дизайн), кто владеет указателем. Если Entity владеет указателем, тогда он должен освободить его в своем деструкторе, как вы делаете (еще лучше; просто оберните его в std::unique_ptr). Если он не владеет указателем, просто не освобождайте его.

Вы не можете иметь это в обоих направлениях. Вы используете shared_ptr, так что подразумевается несколько «владельцев», и, как только последний владелец покончит с этим, память будет освобождена. Опять же, вам необходимо четко определить, кто владеет этой памятью.

Судя по твоему коду, похоже Entity на самом деле не владеет Game*, Это необходимо по причинам реализации, но не должно отвечать за его освобождение.


на заметку, вы нарушаете Правило трех.

2

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