如何使用类体内的键/值创建一次映射(不是每次调用类中的函数)

Posted

技术标签:

【中文标题】如何使用类体内的键/值创建一次映射(不是每次调用类中的函数)【英文标题】:How to create map with keys/values inside class body once (not each time functions from class are called) 【发布时间】:2010-06-24 21:24:56 【问题描述】:

我想创建一个 C++ 类,它允许从 map 中按给定键返回值,并按给定值返回键。我还想将我的预定义地图保留在课程内容中。获取值或键的方法是静态的。如何静态预定义map,防止每次调用getValue(str)函数时创建map?

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 经常被调用,所以我更喜欢使用只创建一次的 map(不是每次调用 other_func 时)。是否必须在 main() 中创建 Mapping 实例,然后在 other_func 中使用它(返回 instance.getValue(str)),还是可以在类主体中定义映射并通过静态函数使用它?

【问题讨论】:

看起来你想要某种形式的单身人士 旁白:您可能没有注意到,但您的 getValue() 函数具有向地图添加额外元素的副作用。要在不修改映射的情况下在映射中查找值,请使用 find() 而不是 operator[] 【参考方案1】:

这是你想要的吗?

#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 "";
        

;

【讨论】:

【参考方案2】:

首先,您可能需要查找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);

与预先创建和使用对象相比,这仍然有一点开销,但它应该比每次重新创建和重新初始化地图(或其他)要少得多。

【讨论】:

+1 指出 Boost.Bimap 简洁地回答了该主题的第一个问题。对于惰性构造部分,我建议修改静态成员以保存 map* (或者更好的是,一个 scoped_ptr)。这将消除对这个 inited 变量的需要,但更重要的是,它可能使代码更安全、更便携。我不知道标准是否保证 map 在构建时不会访问任何其他全局数据。它当然感觉更安全,并遵循访问即初始化规则,使用指针进行操作并根据需要分配映射。 你在哪里设置inited = true @BrunoBieri:哎呀——就在你完成初始化之后。【参考方案3】:

如果您经常从键中查找值,您会发现维护与第一个映射并行的第二个映射更容易且更有效。

【讨论】:

如果是 dotnet 你可能只使用MultiKeyDictionary【参考方案4】:

您不需要创建静态地图,尤其是如果您想创建多个 Mapping 对象。您可以在 main() 中创建您需要的对象,并通过引用传递它,如下所示:

string other_func(Mapping &mymap, string str)

   return mymap.getValue(str);

当然,这会引发关于效率的问题,因为有很多 strings 被复制,所以您可能只想直接调用 getValue 而无需调用 other_func. 的额外开销

此外,如果您对 Boost 库有所了解,那么您可能需要阅读 Boost.Bimap,这就是您在此处实现的内容。

http://www.boost.org/doc/libs/1_42_0/libs/bimap/doc/html/index.html

【讨论】:

【参考方案5】:

静电不好。不。此外,在未找到时抛出或返回 NULL 指针,而不是返回空字符串。 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++ 用户不使用的东西(除非他们不懂该语言),例如单例、静态等。

【讨论】:

以上是关于如何使用类体内的键/值创建一次映射(不是每次调用类中的函数)的主要内容,如果未能解决你的问题,请参考以下文章

Java Dictionary 类存储键值

内联函数详解

在基类体内声明但通过派生类调用的函数的内联

如何使用类属性映射休眠中的列?

如何让 OpenMP 在程序每次运行时只创建一次线程?

JPA工具类