【问题标题】:std::vector overwriting final value, rather than growing?std::vector 覆盖最终值,而不是增长?
【发布时间】:2011-01-11 20:27:19
【问题描述】:

我遇到了一个问题,使用 vector.push_back(value) 会覆盖最终值,而不是附加到末尾。为什么会发生这种情况?我在向量中有一个样本项,所以它的大小永远不会为零。下面是代码..

void UpdateTable(vector<MyStruct> *Individuals, MyStruct entry)
{
    MyStruct someEntry;
    bool isNewEntry = true;

    for (int i = 0; i < Individuals->size(); i++)
    {
        if (!(strcmp(Individuals->at(i).sourceAddress, entry.sourceAddress)))
        {
            isNewEntry = false;
            //snip.  some work done here.
        }
    }

    if(isNewEntry)
    {
        Individuals->push_back(entry);
    }
}

这让我的第一个“样本”值保持不变,并且只允许在向量中再添加一个项目。当添加 2 个新条目时,第二个会覆盖第一个,因此大小永远不会大于 2。

编辑:更多代码,因为这显然不是问题?

void *TableManagement(void *arg)
{
      //NDP table to store discovered devices.
      //Filled with a row of sample data.
      vector<MyStruct> discoveryTable;
      MyStruct sample;
      sample.sourceAddress = "Sample";
      sample.lastSeen = -1;
      sample.beaconReceived = 1;
      discoveryTable.push_back(sample);

      srand(time(NULL));
      while(1)
      {
          int sleepTime = rand() % 3;
          sleep(sleepTime);
          MyStruct newDiscovery = ReceivedValue();
          if (newDiscovery.lastSeen != -1000) //no new value from receivedValue()
          {
              UpdateTable(&discoveryTable, newDiscovery);
          }
          printTable(&discoveryTable);
      }
      return NULL;
}

【问题讨论】:

  • 你为什么使用 strcmp() 和迭代,而不是某种形式的关联容器?您还不必要地覆盖了 isNewEntry。
  • 问题不在你展示的代码上,所以请展示更多代码,具体是如何使用函数的。
  • 你如何检查这个?我非常怀疑push_back 是否会随意覆盖某些东西。人们会告诉你,我会同意:使用 C++! std::stringstd::find 和更合适的容器可能都在商店中。
  • 如何计算向量的大小?您使用指针而不是传递引用的任何原因?
  • 您还应该通过引用而不是指针传递向量(只是样式问题)。

标签: c++ stl vector standard-library


【解决方案1】:

我会冒险猜测:

假设MyStruct 被声明为

struct MyStruct
{
    const char *sourceAddress;
    // Other Gubbins ...
};

ReceivedValue 做了类似的事情

MyStruct ReceivedValue()
{
    static char nameBuffer[MAX_NAME_LEN];

    // Do some work to get the value, put the name in the buffer

    MyStruct s;
    s.sourceAddress = nameBuffer;
    // Fill out the rest of MyStruct
    return s;
}

现在,您推入向量中的每个结构都有 sourceAddress 指向同一个全局缓冲区,每次您调用 ReceivedValue 它都会用新字符串覆盖该缓冲区 - 所以向量中的每个条目最终都相同字符串。

如果没有看到您的其余代码,我无法确定,但我可以肯定,如果您按照 cmets 中的一些好的 C++ 风格建议来解决您的问题,这种可能性就会消失。

为澄清而编辑:无需堆分配您的结构,只需将 sourceAddress 声明为 std::string 就足以消除这种可能性。

【讨论】:

  • 只需将其声明为 char 数组也可以解决问题,并允许他保留现有代码。
  • 再看一遍,将“Sample”分配给sourceAddress 表明sourceAddress 必须声明为const char*,而strcmp 不能是std::string。只要数组中没有覆盖“样本”(我认为我无法用这个答案解释),这就会使其成为问题的可能候选者。
【解决方案2】:

您推送到数据库的项目的范围即将到期。当您离开创建它们的 {} 时,它们将被破坏 - 因此对它们的引用不再有效。

您需要将其从 vector&lt;MyStruct&gt; 更改为 vector&lt;MyStruct*&gt;(最好使用来自 Boost:: 的安全指针,而不是指针,但您明白了)。

您正在(有限)范围内创建项目并将其推送到向量上(在复制 struct 时,其中的字符串是 not!)它然后重用相同的内存位置(如果经过适当优化,很有可能)来存储下一个“新”结构和之后的结构,依此类推。

相反,在有限范围内创建MyStruct *myObject = new MyStruct 并分配其值,然后将指针 推送到向量。

在清除/销毁它之前,请记住 delete 向量中的所有值!!

或者,当然,您可以使用 std::string/CString/whatever 代替 char 数组,并通过安全复制结构完全避免该问题。

【讨论】:

  • 我花了一段时间才得到这个,所以这里有一个注释来澄清:你将地址存储为一个字符数组,这意味着结构实际上唯一知道的是内存地址(即指向)数组。当循环开始时,数组已经超出范围,因此指针(保存在向量中)无效。巧合的是,同一块内存被重新用于新结构的地址字段。所以老指针再次巧合地变为有效,这次指向新地址;这就是 IsNewEntry 测试失败并且没有添加新结构的原因。
  • @Computer 如果您将一个项目推入向量中,则会生成一个副本。没有您描述的范围问题。
  • 但是被复制的是一个 char* - 而不是整个 char 数组。然后指针指向的数组超出范围。
  • @Tom 如果向量使用指针,这也是正确的。而且由于 OP 没有发布他的结构定义,并且可能使用数组而不是指针,我们不能说字符指针是否是问题所在。但如果是,这个答案并没有解决它。
  • 也许吧。由于我们看不到 MyStruct 正在处理 sourceAddress 字段,因此我们无法知道。这个响应是错误的,因为被推送到向量上的结构是本地结构的 /copies/。将向量更改为使用 MyStruct 指针会使问题变得更糟,因为这样向量将保存指向 MyStruct 的指针,其范围已过期,因此已被销毁。如果原始的正在删除它的析构函数中的 sourceAddress char 数组,则副本保存指向已释放 char 数组的指针,并且所有赌注都关闭。
【解决方案3】:

ComputerGuru 的答案在另一种选择中有效。您可以为 MyStruct 创建一个复制构造函数和重载 operator=。在这些操作中,您需要将实际字符串复制到新结构中。在 C++ 中,结构只不过是具有默认公共访问权限而不是默认私有访问权限的类。另一种选择是使用 std::string 而不是 char* 作为字符串值。 C++ 字符串已经有这种行为。

struct MyStruct {
   std::string sourceAddress;
   int lastSeen;
   int beaconReceived;
};

【讨论】:

    【解决方案4】:

    对我来说似乎很奇怪:也许代码的 //snip 部分有问题?

    尝试在push_back 调用之前和之后记录向量的大小(在调试器中或使用cout),并查看isNewEntry 变量。

    【讨论】:

    • //snip 部分没有什么重要的。但是,我已经附加了调用方法。
    【解决方案5】:

    您的代码在我看来没问题。您是否有可能没有传递正确的向量?我的意思是,如果在您尝试添加第三个条目之前以某种方式将您的个人向量重置为其原始的 1 条目状态,您描述的行为就会出现,那么看起来好像您的第二个条目被覆盖了。

    这就是我的意思:

    int test_table()
    {
      string SampleAddresses[] = {"Sample Address 1", "Sample Address 2"};
    
      for (int i = 0; i < 2; i++)
      {
         // All this work to build the table *should* be done outside the loop; but we've accidentally put it inside
         // So the 2nd time around we will destroy all the work we did the 1st time
         vector<MyStruct> Individuals;
         MyStruct Sample;
         Sample.sourceAddress = "Sample Address 0";
         Test.push_back(Sample);
    
         // this is all we meant to have in the loop
         MyStruct NewEntry;
         NewEntry.sourceAddress = SampleAddresses[i];
         UpdateTable(Individuals, NewEntry);
      }
    
      //Now the table has 2 entries - Sample Address 0 and Sample Address 2.
    }
    

    如果这是您的全部代码,那么问题将很明显。但它可能隐藏在其他一些代码中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      • 1970-01-01
      相关资源
      最近更新 更多