Как создать карту с ключами / значениями внутри тела класса один раз (не каждый раз, когда функции из класса вызываются)
Я хотел бы создать класс C++, который позволял бы возвращать значение по заданному ключу из карты и ключ по заданному значению. Я также хотел бы сохранить свою предопределенную карту в содержании класса. Методы для получения значения или ключа будут статическими. Как предварительно определить карту статически, чтобы предотвратить создание карты каждый раз, когда я вызываю функцию getValue(str)?
class Mapping
{
static map<string, string> x;
Mapping::Mapping()
{
x["a"] = "one";
x["b"] = "two";
x["c"] = "three";
}
string getValue(string key)
{
return x[key];
}
string getKey(string value)
{
map<string, string>::const_iterator it;
for (it = x.begin(); it < x.end(); ++it)
if (it->second == value)
return it->first;
return "";
}
};
string other_func(string str)
{
return Mapping.getValue(str); // I don't want to do: new Mapping().getValue(str);
}
Функция other_func вызывается часто, поэтому я бы предпочел использовать карту, которая создается только один раз (не каждый раз, когда вызывается other_func). Нужно ли создавать экземпляр Mapping в main(), а затем использовать его в other_func (возвращать instance.getValue(str)) или можно определить карту в теле класса и использовать ее статическими функциями?
5 ответов
Это то, что вы хотите?
#include <map>
#include <string>
class Mapping
{
private:
// Internally we use a map.
// But typedef the class so it is easy to refer too.
// Also if you change the type you only need to do it in one place.
typedef std::map<std::string, std::string> MyMap;
MyMap x; // The data store.
// The only copy of the map
// I dont see any way of modifying so declare it const (unless you want to modify it)
static const Mapping myMap;
// Make the constructor private.
// This class is going to hold the only copy.
Mapping()
{
x["a"] = "one";
x["b"] = "two";
x["c"] = "three";
}
public:
// Public interface.
// Returns a const reference to the value.
// The interface use static methods (means we dont need an instance)
// Internally we refer to the only instance.
static std::string const& getValue(std::string const& value)
{
// Use find rather than operator[].
// This way you dont go inserting garbage into your data store.
// Also it allows the data store to be const (as operator may modify the data store
// if the value is not found).
MyMap::const_iterator find = myMap.x.find(value);
if (find != myMap.x.end())
{
// If we find it return the value.
return find->second;
}
// What happens when we don;t find anything.
// Your original code created a garbage entry and returned that.
// Could throw an exception or return a temporary reference.
// Maybe -> throw int(1);
return "";
}
};
Прежде всего, вы можете поискать Boost::MultiIndex и / или Boost:: bimap. Любой из них, вероятно, немного поможет в вашей ситуации, когда вы захотите использовать один из парных элементов для поиска другого (bimap - это более прямое, чем вы хотите, но если вам может понадобиться добавить третий, четвертый и т. Д. Ключ, тогда MultiIndex может работать лучше). В качестве альтернативы, вы можете просто использовать пару отсортированных векторов. В таких ситуациях, когда данные остаются постоянными после их заполнения, они, как правило, позволяют быстрее выполнять поиск и использовать меньше памяти.
Оттуда (даже если вам не нужно делать это явно), вы можете обрабатывать инициализацию самого объекта карты немного как одиночный - поместить данные в первый раз, когда это необходимо, и с тех пор просто использовать их:
class Mapping {
static map<string, string> x;
static bool inited;
public:
Mapping() {
if (!inited) {
x["a"] = "one";
x["b"] = "two";
x["c"] = "three";
inited = true;
}
}
string getValue(string const &key) { return x[key]; }
};
// This initialization is redundant, but being explicit doesn't hurt.
bool Mapping::inited = false;
map<string, string> Mapping::x;
С этим твой some_func
может выглядеть примерно так:
string some_func(string const &input) {
return Mapping().getValue(input);
}
Это по-прежнему имеет небольшие издержки по сравнению с предварительным созданием и использованием объекта, но это должно быть намного меньше, чем повторное создание и повторная инициализация карты (или чего-либо еще) каждый раз.
Если вы будете часто искать значение по ключу, вам будет проще и эффективнее поддерживать вторую карту параллельно с первой.
Вам не нужно создавать статическую карту, особенно если вы хотите создать несколько Mapping
объекты. Вы можете создать объект в main() там, где он вам нужен, и передать его по ссылке, как в:
string other_func(Mapping &mymap, string str)
{
return mymap.getValue(str);
}
Конечно, это поднимает вопросы об эффективности, с большим string
копируется, так что вы можете просто позвонить getValue
напрямую без дополнительных затрат на звонки other_func.
Кроме того, если вы знаете что-нибудь о библиотеках Boost, то вы можете прочитать о Boost.Bimap, что-то вроде того, что вы здесь реализуете.
http://www.boost.org/doc/libs/1_42_0/libs/bimap/doc/html/index.html
Статика это плохо. Не. Также, бросить или вернуть NULL указатель на not found, не вернуть пустую строку. Other_func должен быть методом-членом объекта Mapping, а не статическим методом. Все это отчаянно должно быть объектом.
template<typename Key, typename Value> class Mapping {
std::map<Key, Value> primmap;
std::map<Value, Key> secmap;
public:
template<typename Functor> Mapping(Functor f) {
f(primmap);
struct helper {
std::map<Value, Key>* secmapptr;
void operator()(std::pair<Key, Value>& ref) {
(*secmapptr)[ref.second] = ref.first;
}
};
helper helpme;
helpme.secmapptr = &secmap;
std::for_each(primmap.begin(), primmap.end(), helpme);
}
Key& GetKeyFromValue(const Value& v) {
std::map<Value,Key>::iterator key = secmap.find(v);
if (key == secmap.end())
throw std::runtime_error("Value not found!");
return key->second;
}
Value& GetValueFromKey(const Key& k) {
std::map<Key, Value>::iterator key = primmap.find(v);
if (key == primmap.end())
throw std::runtime_error("Key not found!");
return key->second;
}
// Add const appropriately.
};
Этот код использует объект функции для инициализации карты, переворачивает ее для вас, а затем предоставляет методы доступа к содержимому. Что касается того, почему вы используете такую вещь, в отличие от необработанной пары std::maps, я не знаю.
Глядя на код, который вы написали, я догадываюсь, что вы происходят из Java. В Java есть много вещей, которые пользователи C++ не используют (если они не знают язык), такие как одиночные игры, статика и тому подобное.