【问题标题】:Weird behavior with map.find and a pointer to a vectormap.find 和指向向量的指针的奇怪行为
【发布时间】:2012-07-12 17:07:10
【问题描述】:

我有一个向量对的映射,如下所示:

std::map<std::pair<uint16, uint16>, std::vector<std::vector<uint32> > >

地图在类的构造函数中填充。该类提供了一个公共方法,该方法返回指向std::vector&lt;std::vector&lt;uint32&gt; &gt;(映射值部分)的指针,如下所示:

typedef std::pair<uint16, uint16> key;
typedef std::vector<std::vector<uint32> > value;

value* FindValues(key someKey) {
    std::map<key, value>::const_iterator it;
    it = someStore.find(someKey);
    if (it != someStore.end()) 
        return &(value)it->second;
    return NULL;
}

这就是它变得奇怪的时候。当迭代 FindValues 返回的向量时,所有子向量都有一个大的负数(例如 -1818161232)作为它们的第一个值。但如果我使用如下函数:

value FindValues(key someKey) {
    std::map<key, value>::const_iterator it;
    return someStore.find(someKey)->second;
}

...那么数值是正常的。这只发生在 all 子向量的索引 0 处的值。但是,使用第二种方法时,如果找不到密钥(原因很明显),我的应用程序会出现段错误。我做错了什么?

【问题讨论】:

  • 为什么在取地址之前要把it-&gt;second 转换成value
  • @Kliest,我也想问同样的问题。从逻辑上讲,这不应该是一个问题,但我总是警惕C 风格的演员表在出现微妙和不明显的问题时出现。
  • 强制转换的结果不是右值吗?
  • gcc 4.6.1 编译时会发出警告。
  • @Kleist:他这样做是因为他使用const_iterator 进行搜索。出于这个原因,return &amp;it-&gt;second 根本无法编译。所以,他决定用那个演员“覆盖”这个错误。

标签: c++ pointers map vector


【解决方案1】:

如果返回语句真的看起来像

return &(value) it->second;

那么有几件事可以说:

  1. 如果编译器接受它而不发出诊断消息,它就会损坏。在 C++ 中,将内置一元 &amp; 应用于非引用强制转换的结果是非法的。 (value) it-&gt;second 表达式产生一个临时对象,一个右值。您无法使用&amp; 获取此类对象的地址。代码甚至不应该编译。

  2. 如果你的编译器接受它作为某种奇怪的“扩展”,那么这意味着你确实在获取并返回一个临时对象的地址。然后立即销毁临时对象,使您的指针指向垃圾。难怪你会通过这样的指针看到一些奇怪的值。

  3. 由于您使用const_iterator 来存储搜索结果,因此需要进行某种类型的转换。显然,您错误地尝试使用 (value) 演员来抛弃 it-&gt;second 的常量。正确的做法可能如下所示

     return const_cast<value *>(&it->second);
    

    但是你为什么首先使用const_iterator?正确的做法是使用常规的iterator 并执行

     return &it->second;
    

    没有任何额外的演员表。

  4. 您需要决定您要编写哪种FindValue 方法。如果这应该是一个常量方法,它应该返回 const value * 并且应该声明为 const

    const value* FindValues(key someKey) const
    

    当然,在这种情况下,您应该在内部使用const_iterator

    如果您的FindValue 应该是一个非常量方法,那么您可以保留当前声明

    value* FindValues(key someKey)
    

    但是在里面使用普通的iterator

    你现在拥有的是两者的某种混合,这就是让你求助于奇怪演员的原因。 (事实上​​,你的班级可能需要这两个版本。一个可以通过另一个实现。)

【讨论】:

    【解决方案2】:

    您的typedefs 非常具有误导性。这是错误的行:

    return &(value)it->second;
    

    看似简单的 C 风格类型转换实际上是对std::vector 的复制构造函数的调用。这行可以改写为

    return &std::vector<std::vector<uint32> >(it->second)
    

    当您将这一行重写如下时,奇怪结果的原因变得显而易见:

    std::vector<std::vector<uint32> > result (it->second);
    return &result;
    

    您实际上是在返回一个本地对象的地址,该对象将在函数返回后立即销毁。

    【讨论】:

    • 可以有一个非常量 value *,因为修改值不会影响键...
    • 我的错,你是对的。当然可以安全地修改值。我被另一个用户提出 const_cast 而不是 C 转换感到困惑。
    【解决方案3】:

    所以,这个变种会更好。

    typedef std::pair<uint16, uint16> key;
    typedef std::vector<std::vector<uint32> > value;
    
    value* FindValues(key someKey) {
        std::map<key, value>::const_iterator it;
        it = someStore.find(someKey);
        if (it != someStore.end()) 
            return &const_cast<value&>(it->second);
        return 0;
    }
    

    【讨论】:

    • 如果你只是想抛弃 const 为什么要使用const_iterator——为什么不使用iterator呢?如果someStore 是 const,那么在任何情况下尝试修改该值都会导致问题——在这种情况下,您应该让FindValues 返回const value *(同样,不需要 const_cast)跨度>
    猜你喜欢
    • 2013-03-31
    • 1970-01-01
    • 2011-08-04
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多