У меня есть более 300 классов. Они связаны в некотором роде.
Для простоты все отношения равны 1:1. Вот примерная диаграмма.
(В реальном случае их около 50 -отношения пар.)
Примечание.. В некоторых случаях может существовать некоторая связь.
Например, некоторые hen
не относятся к какому-либо food
.
Примечание2: Нет ссылки = никогда, например. каждый egg
не относится ни к какому cage
.
Такое отношение никогда не будет добавлено/удалено/запрошено.
Вопрос:
Как хранить отношения между ними элегантно?
Кажется, что у всех 4 моих идей (ниже) есть недостатки.
Здесь - связанный вопрос, но с отношением 1: N и только 1.
Мои плохие решения
Это полу-псевдокоды.
Версия 1 Прямая
Моя первая мысль - добавить указатели друг к другу.
Chick.h: -
class Egg;
class Food;
class Chick{ Egg* egg; Food* food;}
Hen.h: -
class Egg; class Cage; class Food;
class Hen{ Egg* egg; Cage* cage; Food* food;}
Очень дешево добавлять/удалять отношение и запрос, например.: -
int main(){
Hen* hen; ... Egg* egg=hen->egg;
}
Это работает хорошо, но по мере того, как моя программа растет, я хочу отделить их.
Грубо говоря, Hen.h
не должно содержать слова egg
и наоборот.
Есть много идей, но никто не кажется очень хорошим.
Я покажу краткий фрагмент для каждого рабочего дня, а затем подытоживаю плюсы и минусы в конце вопроса.
Версия 2 Hash-map
Используйте std::unordered_map
.
Это будет шея бутылки моей программы. (профилированный в режиме выпуска)
class Egg{}; class Hen{}; //empty (nice)
.....
int main(){
std::unordered_map<Hen*,Egg*> henToEgg;
std::unordered_map<Egg*,Hen*> eggToHen;
....
Hen* hen; ... Egg* egg=henToEgg[hen];
}
Версия 3 Посредник-одиночный
Храните все отношения в одном большом медиаторе для каждого объекта.
Извлеките большую память для пустых слотов (например, egg
имеет слот henFood_hen
).
Total waste = type-of-relation-pair
* 2 * 4 байта (если выполняется в 32 бита) в каждом объекте.
class Mediator {
Egg* eggHen_egg=nullptr;
Hen* eggHen_hen=nullptr;
Hen* henFood_hen=nullptr;
Food* henFood_food=nullptr;
//... no of line = relation * 2
};
class Base{public: Mediator m;};
class Egg : public Base{}; //empty (nice)
class Hen : public Base{};
int main(){
Hen* hen; ... Egg* egg=hen->eggHen_egg;
}
Версия 4 Посредник-массив (аналогично 3)
Попробуйте стандартизировать - высокая гибкость.
class Mediator {
Base* ptrLeft[5];
Base* ptrRight[5];
};
class Base{public: Mediator m;};
class Egg : public Base{}; //empty (nice)
class Hen : public Base{};
int main(){
enum RELA_X{RELA_HEN_EGG,RELA_HEN_CAGE,RELA_EGG_CHICK, .... };
Hen* hen; ...
Egg* egg=hen->m.ptrRight[RELA_HEN_EGG];
//^ get right of "hen-egg" === get "egg" from "hen"
//^ can be encapsulated for more awesome calling
}
Плюсы и минусы
Зеленый (+
) хорош. Красный (-
) плох.
Изменить: Я использую Entity-Component для игры 60fps.
Это постоянная база данных: один экземпляр, используемый для всей жизни игры.
Edit2:. Все отношения - это слабое отношение, а не свойство a-a или strong std::unique_ptr
. (Спасибо Уолтеру)
- A
hen
находится в acage
.
Некоторыеhens
не находятся ни в одномcage
, а некоторыеcages
пустые. - A
chick
исходит от aegg
.
Однако некоторыеchicks
не исходили ни от какихegg
(они просто удалены с неба),
и некоторымeggs
не повезло статьchick
. - A
hen
и achick
едят a (вероятно, такую же) пластинуfood
.
Некоторые пластиныfood
только что приготовлены, но не поданы.
Edit3: Назначение целочисленного идентификатора для каждого объекта может быть хорошей идеей.
(Спасибо Оливу, ахокса и Симоне Чифани)
Edit4:: Не нужно предоставлять компилируемый код, достаточно существенной части/концепции.