Допустим, у меня есть следующие структуры данных:
struct Base
{
Base(const int id, const std::string &name, const std::string &category):
id(id), name(name), category(category) {}
int id;
std::string name;
std::string category;
};
struct A : public Base
{
A(const int id, const std::string &name, const std::string &category,
const int x, const int y) :
Base(id, name, category), x(x), y(y) {}
int x, y;
};
Я хочу создать не замужем фабричный метод, который возвращает вектор производных классов, где идентификатор, имя и категория известны в функции. Проблема, с которой я сталкиваюсь — нарезка …
std::vector< Base* > getVector(...)
Данные членов структуры A потеряны! (dynamic_cast обратно в A приемлемо в производственном коде?)
Итак, у меня есть этот метод шаблона, но я все еще не считаю его лучшим решением:
template< class T >
std::vector< T > getVector()
{
std::vector< T > retVal;
retVal.push_back(T(45, "The Matrix", "Science Fiction"));
retVal.push_back(T(45, "Good Luck Chuck", "Comedy"));
...
return retVal;
}
Есть ли лучшее решение, кроме метода шаблона?
То, что вы просите, кажется сомнительным, потому что вы хотите:
Так что нет способа заставить его работать прилично. Код вашего шаблона все еще должен статически знать тип вашего объекта на сайте вызывающей стороны, поэтому создание объекта не отвлекается.
Мой совет: подумайте еще раз о своей проблеме: почему вы хотите иметь динамичный завод? Почему не все объекты имеют общие средства доступа в Base
учебный класс?
Если вы не можете четко ответить на эти два вопроса, это может означать, что вы не должны иметь эту иерархию классов в первую очередь.
Я на самом деле думаю, что ваш шаблон, вероятно, лучшее решение, по крайней мере, это идиоматический C ++.
Использование RTTI, таких как dynamic_cast
это тоже нормально, но это довольно менее безопасно (проверка времени выполнения или проверка типов во время компиляции) и часто менее эффективно. Однако иногда — или довольно часто на самом деле — вы необходимость чтобы полиморфизм определялся только во время выполнения (например, когда вам нужны разные производные объекты в одном и том же std::vector
и не может использовать фиксированную длину std::tuple
). Тогда вы, как вы уже подготовились, можете обойти проблемы нарезки, используя вектор базового класса. указатели а не предметы. Проблема с (простыми) указателями заключается в том, что они в некоторой степени обходят автоматическое управление памятью в C ++: когда std::vector<Base*>
выходит за рамки видимости, объекты, на которые указывают не удаляется, оставаясь торчащим где-то недоступным: у вас утечка памяти. Вот почему такие языки, как Java, которые в большей степени полагаются на этот способ использования наследования, являются сборщиком мусора. Эта проблема обсуждается в другом месте, рекомендуемое решение — заменить Base*
с std::unique_ptr<Base>
,
После того, как вы это запустите, вы сможете использовать полиморфные функции времени исполнения. Ты должен нормально не необходимость dynamic_cast
, а лучше написать все, где разные производные экземпляры различаются как виртуальные функции-члены