Я делаю игру о мраморе, которая использует структуру для описания свойств мрамора:
struct Marble
{
Color color;
int weight;
//Other customizable marble properties
};
Я в настоящее время использую std::vector
чтобы отслеживать, какие шарики находятся в какой позиции на игровом поле:
std::vector<Marble> m_marbles;
В любое время я могу проверить свойства мрамора с учетом его положения путем индексации по вектору. Чтобы проверить цвет мрамора в позиции 20:
m_marbles[20].color;
Пользователь может создавать собственные шарики и добавлять их на игровую доску. Если я хочу добавить новый мрамор на игровое поле, я создаю новый объект структуры и помещаю его в вектор:
Marble m;
//Fill struct with marble information
m_marbles.push_back(m)
Проблема в том, что игровое поле очень большое (много тысяч шариков), и многие шарики точно такие же.
Допустим, на игровой доске 4000 позиций. Возможно, 3000 из них — красный мрамор весом 1,0. В текущем решении 3000 эквивалентных объектов Marble занимают место в моем векторе.
Поскольку пользователь может определять свои собственные типы мрамора, я не могу просто жестко закодировать все возможные типы мрамора, а затем заставить вектор хранить указатель на соответствующий мраморный объект.
Я думал об использовании std::set
хранить разные виды мрамора. Затем, когда пользователь отправляется на создание своего собственного мрамора, он может попытаться вставить его в набор. Если такой же тип мрамора уже существует, он не будет тратить память.
У меня вопрос, как я могу сохранить ссылку в std::set
? Например, скажем, мой набор состоит из следующих типов мрамора:
{RedMarble, BlueMarble, StrangeMarble, ConfusedMarble}
Теперь я хочу назначить красный мрамор на позицию 20 на игровом поле:
m_marbles[20] = ?
Я также хочу проверить цвет мрамора в позиции 31:
m_marble[31].color;
Можно ли так работать с набором? Функции поиска и вставки возвращают итераторы, но не станут ли они недействительными, если я добавлю больше элементов в набор?
Да, вы можете делать то, что вы пытаетесь сделать. Вы можете хранить указатели в своем векторе, которые указывают на шарики в вашем наборе. Или вы можете хранить итераторы в вашем векторе, опять же, какие итераторы указывают на Marbles в вашем наборе.
Вот как я бы сделал это с помощью указателей:
// A useful helper funtction -- not required, but useful.
const Marble* Helper(const Marble& m) {
static std::set<Marble> uniqueMarbles;
return &*(uniqueMarbles.insert(m).first);
}
// A vector of pointers to marbles
std::vector<const Marble*> m_marbles;
// You can pass temporaries to the helper function, and it returns a permanent pointer
m_marbles.push_back(Helper(Marble(RED, 1.0));
Marble m;
m.setColor(BLUE);
m_marbles.push_back(Helper(m));// You can reference your marbles
Color c = m_marbles[i]->getColor();
// Hopefully the following will produce a compiler error message
m_marbles[i]->setColor(GREEN); // Oops, m_marbles holds const pointers
Вы должны сделать вектор содержать указатели Мраморные конструкции:
std::vector<Marble*> m_marbles;
Указатель не занимает много места, и вы все еще можете использовать Marble
в качестве базового класса.
Если игровая доска на самом деле всего несколько тысяч мест, я бы не стал тратить время впустую. Например
10000 places * sizeof(Marble) = 10000 * 100 bytes ~ 1 MB
Не большой объем памяти по сегодняшним меркам.
Если он может стать очень большим, то вы действительно можете хранить свои уникальные шарики в наборе и ссылаться на них с помощью указателя, как уже показывал @BenRuijl.
Построить класс Marble
содержащий указатель на MarbleType
определение свойств каждого мрамора (для каждого вида мрамора у вас есть один экземпляр MarbleType
). Вектор может содержать Marble
s или общие указатели на Marble
с или уникальные указатели. Если Marble
класс невелик (образец в навесном весе), вы не теряете места, но тем не менее имеете чистое и гибкое решение. Типы мрамора могут быть названы (например, строкой) и храниться в std::map
,