【问题标题】:When insert a new item in stl map, occurs an overwrite to first element在 stl 映射中插入新项目时,会覆盖第一个元素
【发布时间】:2015-01-02 03:43:49
【问题描述】:

首先,我有以下代码:

map<char*, char*> records;

// id, value are buffer variables to store the text that returns from `strcpy()` 
char *id = (char*)malloc(sizeof(char*) * 20);
char *value = (char*)malloc(sizeof(char*) * 20);

sql->open(dbFile);
sql->query("SELECT * FROM categories");
while(sql->fetch() != SQLITE_DONE){

    // copy the text that returns from `sql->getValue`, then convert that text from `const unsigned char*` to `const char*` 
    strcpy(id, reinterpret_cast<const char*>(sql->getValue(0)));
    strcpy(value, reinterpret_cast<const char*>(sql->getValue(1)));

    // insert the values that in `id`,`value` as new item
    records.insert(pair<char*, char*>(id, value));           
}
free(id);
free(value);

前面代码的想法是使用strcopy()复制返回此方法sql-&gt;getValue(0)的文本,然后将该文本从const unsigned char*转换为const char*,然后将这些文本存储在id,@ 987654327@ 变量,然后插入idvalue 作为新项目。 前面的步骤发生在从数据库获取的每个数据上。

进一步澄清这个想法:
例如,从数据库中提取了三行(fruitsvegetablessweets)。
自然,将这三个数据插入地图容器时,容器中的项目数将是三个项目first(fruits),second(vegetables )、第三(sweets)。

在我的情况下,当检查项目数量时,我发现只有一项,项目是sweets

现在,我很惊讶,剩下的物品在哪里?

【问题讨论】:

  • 您用于std::map 的密钥不是id,它是char*id。我建议你使用std::string,因为我认为你也有内存泄漏。
  • 您在上一个问题中被告知,char* 的地图是一个糟糕的设计。可以这样做,但您会遇到内存管理混乱,就像在这段代码中一样。
  • @Galik:谢谢,确切地说,我知道这一点,但不知道如何在不使用字符串类型的情况下解决问题。
  • @Blastfurnace:是的,但我只使用 stl 映射来模拟关联数组。
  • @LionKing:每次调用insert() 时,您都会为键/值对传递相同的地址。这就是std::map 看到的所有内容,两个指针。它不遵循指针来查看字符串或进行复制。您可能会在调用之间修改 char 数组的内容,但 std::map 不知道这一点。在循环之后,您 free() 将在 std::map 中留下悬空指针的内存。

标签: c++ stl stdmap


【解决方案1】:

您将覆盖相同的内存块 3 次,并将指向这些块的指针存储 3 次。就地图而言,每次都是相同的值,因为指针永远不会改变。

看来您真正想要的是拥有一个字符串映射,而不是指针。如果你使用std::string,这会容易得多:

#include <string>

...

map<string, string> records;

string id, value; // size of allocation will be handled by the string class.

sql->open(dbFile);
sql->query("SELECT * FROM categories");
while(sql->fetch() != SQLITE_DONE){
  // string has an assignment operator from C-style strings
  id    = reinterpret_cast<const char*>(sql->getValue(0));
  value = reinterpret_cast<const char*>(sql->getValue(1));

  // more readable way of writing the insert. id, value will be copied into
  // the map.
  records[id] = value;
}

// freeing unnecessary; string class handles it when id, value go out of scope.

这还具有使代码异常安全(至少是字符串)的优点,而无需 try-catch 块。无论作用域如何离开,std::string 的析构函数都会处理清理工作。

【讨论】:

  • 谢谢,但是除了使用字符串类型没有别的办法吗?
  • @LionKing:您可以为插入std::map每个 字符串分配内存,并确保在正确的时间手动释放该内存,并且只释放一次。或者您可以让std::string 为您正确管理该资源...
  • 有,但是你不想用。说真的,它会比癌症更糟糕^W非常糟糕的事情。您必须为循环内的新 C 字符串分配新内存,将值复制到其中,并将它们存储在映射中。您必须手动清理所有这些 C 风格的字符串。您必须对所有这些分配进行错误处理。如果地图要保持排序,您必须为使用 C 样式字符串的地图提供比较函子。你真的不想做那种事。相信我和其他所有人。
  • @Blastfurnace:谢谢,我想我会使用字符串类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-28
  • 1970-01-01
  • 1970-01-01
  • 2020-05-20
  • 2018-09-01
  • 2017-08-24
相关资源
最近更新 更多