Как создать карту с ключами / значениями внутри тела класса один раз (не каждый раз, когда функции из класса вызываются)

Я хотел бы создать класс 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++ не используют (если они не знают язык), такие как одиночные игры, статика и тому подобное.

Другие вопросы по тегам