【问题标题】:std::map with pointers: wrong value assess带有指针的 std::map:错误的值评估
【发布时间】:2014-08-18 16:40:09
【问题描述】:

我正在尝试将指向 Block 对象的指针 保存到 STL 映射中:

void IO::parseInput(void)
{
    map<string, Block*> blocksMap;

    //=== Create new block and write it to the vector in database ===
    Block tmpBlock (bIndex, bName, bWidth, bHeight);
    Block* tmpPointer = db.addBlock(tmpBlock); //addBlock returns pointer to the added Block

    tmpPointer->printParams(); // -- here is correct output

    blocksMap.insert ( pair<string, Block*>(bName, tmpPointer) );

    // === Test hash ===
    blocksMap.find("anyLegalStringKey")->second->printParams(); // -- wrong output
}

DB类中的addBlock函数:

Block* DB::addBlock (Block& newBlock)
{
    blocks.push_back(newBlock);
    Block* ptrToLast = &blocks.back();
    return ptrToLast;
}

DB类中Block的向量:

class DB:
{
    private:
    //=== Database ===
    vector <Block> blocks;
};

问题: 在写入地图之前,我使用指针 tmpPointer 访问对象(我要保存的指针)并打印其所有参数。这个工作正确。然后我用特定的键将此指针保存在地图中。当我尝试使用带有特定键的 map 中的 find 访问相同的指针时,我得到了错误的输出(我还打印了所有参数)。

当我尝试访问 map 中任何现有键的指针时,可能会导致四种不同的反应:

  1. 一切正常,我得到正常输出(在我的例子中,我打印所有参数)
  2. 我得到了错误的参数(例如,我得到了 21814704 而不是索引 7)
  3. 不可读的输出
  4. 分段错误

有趣的是,对于同一个块对象,我总是有相同的反应(例如,名称为“g22i”的块总是有不可读的输出)。

db 中的矢量“块”在我将指针保存在地图中之前和之后包含正确的信息。

谢谢!

【问题讨论】:

  • 当你push_back变成一个vector时,vector可以增长,这就需要重新分配。这会使对其元素的引用和迭代器无效。
  • 您应该始终检查.find 的结果以确保.find() != blocksMap.end()。取消引用 .end() 是未定义的行为,它没有找到任何东西,只要你调用 -&gt;second 就取消引用
  • 将向量中的块存储为指针,最好是unique_ptr&lt;Block&gt;。当推回更多时,指向这些块的指针不会改变。或者,将索引存储在地图中,以便在矢量中查找。
  • 一般建议:使用智能指针 (std::shared_ptr) 而不是裸指针

标签: c++ pointers memory map reference


【解决方案1】:

您正在尝试使用指向std::vector 元素的长寿命指针。这是灾难的秘诀。当你的向量决定重新分配自己时,所有这些指针都会失效。

如果你想使用指向向量元素的指针,你必须确保向量永远不会重新分配。 IE。您必须提前预留足够的容量来存储所有未来的元素。大多数情况下,它不是最理想的,违背了使用 std::vector 的目的,或者根本不可能。

您还可以使用永远不会使指向其元素的指针无效的容器,例如std::list。但是std::list 不支持随机访问。您可以使用std::deque,它支持随机访问(尽管效率低于std::vector),并且只要您不在序列中间插入任何内容,就可以保持元素指针的有效性。

最后,您可以继续使用std::vector,但请确保在其中存储指向Block 对象的[智能] 指针,而不是Block 对象本身。这个想法是确保向量重新分配不会导致实际Block 对象的重新分配。

P.S.另外说明:当你声明一个map&lt;string, Block*&gt; blocksMap时,这种映射的元素类型为pair&lt;const string, Block*&gt;(注意额外的const)。 IE。 map&lt;string, Block*&gt;::value_type 实际上是pair&lt;const string, Block*&gt;

稍后您尝试使用pair&lt;string, Block*&gt; 参数调用blocksMap.insert,而blocksMap.insert 实际上期望pair&lt;const string, Block*&gt;。这会编译,但它涉及由std::pair 的转换构造函数执行的从pair&lt;string, Block*&gt;pair&lt;const string, Block*&gt; 的隐式转换。这在性能方面并不是最优的。这就是为什么一个更好的主意可能是使用 map&lt;string, Block*&gt;::value_type 而不是尝试手动拼写元素类型

blocksMap.insert ( map<string, Block*>::value_type(bName, tmpPointer) );

【讨论】:

    猜你喜欢
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多