Мне нужно создать глобальный список, к которому может обращаться и использовать группа классов, которые я инициализировал, но эти классы должны быть включены для создания списка. Есть идеи?
Пример:
class Game_object
{
public:
~Game_object()
void Update();
void Render();
//Other stuff
};
class Explosion
{
Stuff
};
class Player
{
Stuff
};
Все эти классы должны получить доступ к приведенному ниже списку, который находится в другом заголовочном файле, а также мой int main
std::list <Game_object *> Objects;
std::list <Explosion *> Explosions;
И так далее.
Идея состояла в том, чтобы я мог создать экземпляр взрыва в деструкторе объекта Game_object и поместить его в список. Это было так, чтобы я мог иметь его рендеринг и обновление внутри главного
Ты имеешь в виду Game_object
должен держать std::list<Explosion *>
а также Explosion
должен держать std::list<Game_object *>
? Это звучит как работа для предварительные декларации:
#include <list>
class Game_object;
class Explosion;
class Game_object
{
std::list<Explosion *> l;
};
class Explosion
{
std::list<Game_object *> l;
};
Это работает, потому что ваши списки содержат указатели, не актуальные объекты.
Объявите ваши списки в заголовочном файле (например, mylists.h) следующим образом:
#include <list>
#include <myGameClasses.h>
class Game_object;
class Explosion;
extern std::list<Game_object*> Objects;
extern std::list<Explosion*> Explosions;
// And so for other lists
Использование extern создаст ссылочная декларация для ваших списков. Затем вы должны создать определяющие декларации в исходном файле (например, mylists.cpp):
#include "mylists.h"
std::list<Game_object*> Objects;
std::list<Explosion*> Explosions;
// And so for other lists
Почему ты должен делать это так? Это необходимо для предотвращения нескольких объявлений при включении вашего заголовка (mylists.h). Тогда потребуется только одна декларация, которая, в данном случае, в исходном файле (mylists.cpp).
Чтобы иметь возможность использовать списки, просто включите заголовочный файл.
Кроме того, в mylists.h также необходимо предварительные декларации из ваших классов:
class Game_object;
class Explosion;
Это сделано для того, чтобы избежать ошибок необъявленного идентификатора.
Может быть, нет смысла обращаться к этим спискам, но если вам нужен такой доступ, вы можете сделать это разными способами, наиболее просто с помощью глобальных переменных:
// forward declare Game_object:
class Game_object;
// Declare lists:
typedef std::list <Game_object *> ListObjects;
typedef std::list <Explosion *> ListExplosions;
ListObjects Objects;
ListExplosions Explosions;
class Game_object
{
public:
void Update()
{
// Update Objects...
for (ListObjects::const_iterator O = Objects.begin(); O != Objects.end(); ++O)
{ ... };
// Update Explosions...
for (ListExplosions::const_iterator E = Explosions.begin(); E != Explosions.end(); ++E)
{ ... };
};
void Render()
{
// Render Objects...
for (ListObjects::const_iterator O = Objects.begin(); O != Objects.end(); ++O)
{ ... };
// Render Explosions...
for (ListExplosions::const_iterator E = Explosions.begin(); E != Explosions.end(); ++E)
{ ... };
};
//Other stuff
};
Это не очень хорошая идея, глобальные переменные могут быть проблематичными, а еще хуже — с глобальными списками, к которым могут обращаться другие классы и изменять их содержимое, в то время как другой класс пытается изменить содержимое одновременно; но если вы не используете темы, это может быть не так уж сложно.
Другим подходом может быть создание менеджера. Менеджер должен быть владельцем списка и нуждаться в методах Setters
, Getters
, а также Deleters
методы. Чтобы обновить содержимое списка, вы можете передать ссылку на менеджер в средство визуализации / обновления и получить ссылку на список от менеджера:
class ObjectManager
{
public:
typedef std::list <Game_object *> ListObjects;
void AddObject(const ObjectStuff &os)
{ ... };
const ListObjects &GetObjects()
{ return Objects; };
private:
ListObjects Objects;
}
class ExplosionManager
{
public:
typedef std::list <Explosion *> ListExplosions;
void AddExplosion(const ExplosionStuff &es);
{ ... };
const ListExplosions &GetExplosions()
{ return Explosions; };
private:
ListExplosions Explosions;
}
void Render(const ObjectManager &om)
{
// Ask ObjectManager for the object list
}
void Render(const ExplosionManager &em)
{
// Ask ExplosionManager the explosion list
}
Приведенный выше код является наивным и довольно простым приближением, но только для примера. Преимущество вышеупомянутого подхода заключается в том, что список изменяется только объектом-владельцем, если список необходим из диспетчера и предоставляется только для чтения, а если вы используете потоки, довольно просто добавить блокировки и разблокирует методы менеджера, чтобы избежать изменений при использовании списка.
Это не спрашивается, но я думаю, что стоит сказать: может быть хорошей идеей избежать хранения указателей объектов в контейнерах и изменить тип хранилища для экземпляров объектов:
std::list <Game_object *> VS std::list <Game_object>
Когда вы используете указатели, вы должны позаботиться о распределении и освобождении объектов, если списки являются глобальными, вам понадобится публичный Close
метод для освобождения всей памяти, управляемой указателями, хранящимися в списках, если список принадлежит какому-либо объекту, вам необходимо выполнить тот же процесс в деструкторе объекта.
Но если вы храните экземпляры объектов, деструктор списка освобождает все объекты, хранящиеся по истечении срока его службы, код становится чище и проще в обслуживании.