Я пытаюсь создать общий класс хранения в C ++. Если вы посмотрите на код ниже, я хочу хранить карты string
/ AnyType
и получить к ним доступ.
class StoresTestC
{
public:
template < class SettingsType >
std::map< std::string, SettingsType >* getStore(std::string const&);
private:
std::map< std::string, void* > stores_;
};
template < class SettingsType >
std::map< std::string, SettingsType >* StoresTestC::getStore(std::string const& name)
{
if (stores_.count(name) > 0)
{
void* temp = this->stores_[name];
return (std::map< std::string, SettingsType >*)(temp);
}
else
{
std::map< std::string, SettingsType > * result = new std::map< std::string, SettingsType > ();
this->stores_[name] = result;
return result;
}
}
В этом я вижу две очевидные опасности:
Если я назову это с неправильным SettingsType
/ name
Я назову неправильный актерский состав, который, насколько я знаю (я могу ошибаться), приведет к неопределенному поведению.
Это создаст утечку памяти, но у меня есть решение для этого (которое было слишком долго, чтобы быть раскрытым здесь).
Есть ли что-то еще, что может пойти не так, и вы можете предвидеть?
Сначала сделайте шаг назад и убедитесь, что вы действительно хочу сделать это. Тогда посмотрите на свой дизайн еще раз.
ОК, вам все еще нужна эта возможность?
использование std::map<std::string, boost::any>
где boost::any
всегда map
тип. Затем, когда вы используете any_cast
или какой-либо механизм, чтобы вернуть предмет, вы гарантированно, что это правильный тип или он бросает, так что вы никогда не рискуете неопределенным поведением. Кроме того, так как any
по значению у вас нет возможной утечки памяти.
Следует также отметить, что в вашем оригинальном решении, если вы используете shared_ptr<void*>
было бы помнить, как удалить исходный тип, хранящийся в этом shared_ptr
таким образом устраняя утечку памяти, которую вы упомянули.
РЕДАКТИРОВАТЬ: Я не вижу никаких других очевидных технических проблем с чем-то вроде этого. Однако обратите внимание, что наличие такой карты возможно / может вызвать познавательный («гроккинг») проблемы для будущих сопровождающих, и это несколько увеличивает сложность кода.
Да, вы правы в своих сомнениях по поводу пункта 1 (пункт 2 должен быть решаем, как вы упомянули, но подумайте о том, что деструкторы содержащихся в них карт должны называться правильно).
Я не отпущу такой интерфейс в дикую природу, не предоставив простой механизм, за которым следят клиенты SettingsType
и std::string
Представление ‘typename’ в синхронизации. Простая опечатка может все испортить. я думаю что boost::any
будет просто выражать это с исключением, как только вы попытаетесь привести значение к желаемому / ожидаемому типу результата.
Зачем тебе std::string
представление здесь вообще? Если вы используете RTTI, например, вы можете скрыть ключ, используемый для stores_
с помощью typeid()
,
Так как вам все равно нужно передать тип в качестве параметра шаблона, чтобы получить карту, нужно ли иметь только одну карту для каждого типа?
namespace StoresTest {
template<typename T>
struct MapStruct {
static std::map<std::string, T> the_map;
};
template<typename T> std::map<std::string, T> MapStruct<T>::the_map;
template<typename T>
inline std::map<std::string, T>& getStore()
{ return MapStruct<T>::the_map; }
}
Тогда, например, вы можете получить карту с типом значения Foo
при выполнении
std::map<std::string, Foo>& foo_map = StoresTest::getStore<Foo>();
Вам больше не нужен string
назвать тип, но это может быть добавлено, если у вас есть другие причины, чтобы хотеть его.