【问题标题】:Pointers as keys in map C++ STL指针作为映射 C++ STL 中的键
【发布时间】:2014-09-27 04:16:01
【问题描述】:

我有一个关于在地图中用作键时如何处理指向自定义对象的指针的问题。更具体地说,如果我定义

std::map< CustomClass*, int > foo;

默认的 C++ 实现可以处理这些指针吗?还是我需要定义一个自定义比较器函数来处理它?一般来说,使用指向对象的指针作为键是一种好习惯吗?

【问题讨论】:

  • 任何支持less&lt;T&gt;的东西都可以在地图中使用。见stackoverflow.com/questions/1098966/…
  • 在映射中使用指针作为键的一个可能的缺点是项目的顺序是不确定的(取决于它们的内存地址)。如果您需要以确定的顺序迭代项目,则不能使用指针作为键。这在多人游戏中可能很重要。

标签: c++ pointers map stl


【解决方案1】:

默认实现会比较指针存储的地址,所以不同的对象会被认为是不同的键。但是,不会考虑对象的逻辑状态。例如,如果您使用std::string * 作为键,则具有相同"Hello" 文本的两个不同std::string 对象将被视为不同的键! (当按他们的地址存储在地图中时)

只要你理解上面的重要区别,就可以使用指针作为键。

【讨论】:

  • 是的,这很好!我的假设是指向唯一对象的指针。
【解决方案2】:

指针将被处理,但作为指针进行比较(内存顺序)。如果您想比较对象,则必须通过自定义 less 函子:

template<class T> struct ptr_less {
    bool operator()(T* lhs, T* rhs) {
        return *lhs < *rhs; }};
map<Object*,int,ptr_less<Object>> mymap;

【讨论】:

  • 这应该被选为正确答案。比较指针(几乎总是肯定的)毫无意义,并且肯定会导致极难检测到的错误。
  • 如果您只想将指针用作快速查找地图的键,这很好。通常地图只是用作查找表,并且顺序并不是非常相关。
  • @Andy:我从来没有说过它不是,如果你知道你在做什么(我说:指针将被处理,但比较指针=内存顺序)。如果您希望比较对象,我只添加了可能的解决方案,仅此而已。
  • 无意冒犯@firda - 评论是为了在我上面发表评论。
【解决方案3】:

C++ 标准为指针提供了 std::less 的特化,所以是的,您可以安全地将它们用作映射键等。

【讨论】:

    【解决方案4】:

    除了这个的合法性和任何可能的语义误解,已经解决,我想不出任何理由在这里使用std::map 而不是std::unordered_map。如果您使用的是 C++11 之前的编译器,那么在 Boost 和 Visual C++ 中就会出现这种情况的早期拦截。

    由于您似乎使用指针来表示唯一对象,因此boost::flyweight 之类的内容可能适用。

    【讨论】:

    • 可惜我还没有进入C++11领域!
    【解决方案5】:

    指针可以用作键,但特别是对于 std::map(或 std::set),我不建议这样做。 程序的行为不是确定性的,即当一个人在地图上迭代时,地图中的项目被迭代的顺序不能保证是相同的。它实际上取决于对象(键)的内存地址。看一下这个例子,你可以看到,当键是字符串而不是指针时,无论在映射中的插入顺序如何,项目都会以确定性的方式迭代。

    http://ideone.com/VKirct

    #include <iostream>
    #include <map>
    using namespace std;
    
    class SomeClass {
        public:
        SomeClass(const std::string& name): m_name(name) {}
        std::string GetName()const {return m_name; }
        bool operator <(const SomeClass& rhs) const { return m_name < rhs.m_name; }
        private:
        std::string m_name;
    };
    
    auto print_seq  = [](const auto& seq) { for (const auto& itr: seq) {std::cout << itr.second << " , ";} std::cout << std::endl;};
    
    int main() {
        // your code goes here
        std::map<SomeClass*, std::string> pointer_keyed_map;
        SomeClass s3("object3");
        SomeClass s1("object1");
        SomeClass s2("object2");
        pointer_keyed_map.insert(std::make_pair(&s1, s1.GetName()));
        pointer_keyed_map.insert(std::make_pair(&s2, s2.GetName()));
        pointer_keyed_map.insert(std::make_pair(&s3, s3.GetName()));
        std::cout << "Pointer based keys: object order" << std::endl;
        print_seq(pointer_keyed_map);
    
        std::map<SomeClass, std::string> int_keyed_map;
        int_keyed_map.insert(std::make_pair(s3, s3.GetName()));
        int_keyed_map.insert(std::make_pair(s1, s1.GetName()));
        int_keyed_map.insert(std::make_pair(s2, s2.GetName()));
        std::cout << "String based keys: object order" << std::endl;
        print_seq(int_keyed_map);
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-17
      • 2012-08-04
      • 1970-01-01
      • 2014-09-27
      • 2011-01-09
      • 2015-06-30
      • 2017-08-08
      相关资源
      最近更新 更多