带有指针的map:错误的值评估

std::map with pointers: wrong value assess

本文关键字:错误 评估 map 指针      更新时间:2023-10-16

我试图保存到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;
}

Block在DB类中的向量:

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

问题:在写入映射之前,我使用指针tmpPointer访问对象(我想保存的指针)并打印其所有参数。这个是正确的。然后我把这个指针保存在map中。当我尝试使用特定键的map中查找来访问相同的指针时,我得到了错误的输出(我也打印了所有参数)。

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

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

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

向量"块"在db中包含正确的信息之前和之后,我保存指针在地图。

谢谢!

您正在尝试使用指向std::vector元素的长寿命指针。这是灾难的根源。在vector决定重新分配自身的那一刻,所有这样的指针都将失效。

如果你想使用指向vector元素的指针,你必须确保vector永远不会重新分配。也就是说,你必须提前预留足够的容量来存储所有未来的元素。大多数时候,它不是最优的,违背了使用std::vector或plain的目的。

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

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

注:另一个注意事项:当您声明map<string, Block*> blocksMap时,这种映射的元素具有pair<const string, Block*>类型(注意额外的const)。即map<string, Block*>::value_type实际上是pair<const string, Block*>

后来你试图用pair<string, Block*>参数调用blocksMap.insert,而blocksMap.insert实际上期望pair<const string, Block*>。这可以编译,但它涉及到由std::pair的转换构造函数执行的从pair<string, Block*>pair<const string, Block*>的隐式转换。这不是最优的性能。这就是为什么一个更好的主意可能是使用map<string, Block*>::value_type而不是试图手动拼写元素类型

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