在向量中使用擦除时双重释放或损坏(fasttop)。知道它们的索引,你怎么能擦除向量的几个项目?

Posted

技术标签:

【中文标题】在向量中使用擦除时双重释放或损坏(fasttop)。知道它们的索引,你怎么能擦除向量的几个项目?【英文标题】:double free or corruption (fasttop) when using erase in vector. How can you erase several items of a vector knowing their indexes? 【发布时间】:2020-05-13 03:08:02 【问题描述】:

我正在从事一个“大”项目,但遇到了分段错误,我最终将其缩小为一个更简单且可重现的示例:

我要放两段代码。第一:

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()


    std::vector<int> someVector;

    printf("1\n");
    someVector.push_back(1);

    printf("2\n");
    someVector.push_back(2);

    printf("3\n");
    someVector.push_back(3);


    someVector.erase(someVector.begin());
    someVector.erase(someVector.begin());

    return 0;

在这里,一切正常。行。现在让我们尝试一些更复杂的东西。让我们使用我自己定义的类的向量,而不是使用整数向量。

注意:我不是专家,在定义类时可能会出错。之所以在类定义中有一个拷贝构造函数是因为parameters变量,实际上是一个指向另一个类的指针(而不是一个int),我把它拷贝到了构造函数的主体上真实的例子。

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class ActionType 
  public:
    std::string name;
    int type;
    int * parameters;

    ActionType(const std::string _name, const int _type
        );

    ActionType(const ActionType &);   

    ~ActionType()
        // Direct implementation of destructor
        printf("DELETING\n");
        delete parameters;
    

  ;

 // Implementation of initializer      
 ActionType::ActionType(const std::string _name, const int _type)
        : name(_name)
        , type(_type)
        , parameters(new int(5))



 // Implementation of copy constructor
 ActionType::ActionType(const ActionType & actionType)
     : name(actionType.name)
      , type(actionType.type)
      , parameters(new int(*actionType.parameters))


    printf("COPYING\n");


int main()

    ActionType actionType("foo", 1);

    std::vector<ActionType> actions;

    printf("1\n");
    actions.push_back(actionType);

    printf("2\n");
    actions.push_back(actionType);

    printf("3\n");
    actions.push_back(actionType);

    actions.erase(actions.begin());
    actions.erase(actions.begin());

    return 0;

这应该几乎相同,但会引发错误:

*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001618c70 ***                             
Aborted (core dumped)

问题是,在我的真实示例中,我需要一种方法来删除向量的多个项目,为此我有类似的方法:

for (int i (...))
    int indexToDelete = getIndex();
    vector.erase(vector.begin()+indexToDelete);

我也可以使用类似的东西:

std::vector<int> indexes;
for (int i (...))
    int indexToDelete = getIndex();
    indexes.push_back(indexToDelete);

vector.erase(indexes);

但我还没有想到任何功能可以做到这一点。我看过其他答案,他们使用 sort 将所有要删除的项目放在最后以及在他们调用 pop_back 之后,但它看起来不是很干净。

无论如何,在总结中:

有人知道为什么在使用带有矢量的类时,函数擦除不能正常工作吗?

如何在知道向量中的索引的情况下删除多个项目?

【问题讨论】:

您缺少一个复制分配运算符来完成Rule of Three。 问题不是std::vector -- int main() ActionType a1("foo", 1); ActionType a2("foo2",2); a2 = a1; 砰,你死定了。 See this @PaulMcKenzie,不要因为编程错误而杀死这个可怜的家伙 :) 您的编辑实际上不起作用,因为您已经创建了内存泄漏。您需要以某种方式、形状或形式销毁旧内容,而您的代码不会这样做。我给出的答案显示了如何正确地做到这一点,给定一个正确的复制构造函数和析构函数。 @RSahu -- 好吧,程序肯定死了。 【参考方案1】:

您的课程缺少赋值运算符。 std::vector 要求您的类具有正确的复制语义,而您的 ActionType 没有正确的复制语义。

为您编写的类添加赋值运算符很简单:

#include <algorithm>
//...
class ActionType

   //...
   ActionType& operator=(const ActionType &);
   //...
;

ActionType& ActionType::operator=(const ActionType &rhs)

    if ( this != &rhs )
    
        ActionType temp(rhs);
        std::swap(temp.name, name);
        std::swap(temp.type, type);
        std::swap(temp.parameters, parameters);
      // <-- The temp is destroyed here, along with the contents.
    return *this;

上面使用copy / swap 成语来实现赋值运算符。

注意添加赋值运算符时的代码now works。

编辑:

赋值运算符的另一种形式可以是:

class ActionType

   //...
   ActionType& operator=(ActionType);
   //...
;

ActionType& ActionType::operator=(ActionType temp)

    std::swap(temp.name, name);
    std::swap(temp.type, type);
    std::swap(temp.parameters, parameters);
    return *this;

【讨论】:

谢谢!如果您不介意,几个问题: 1. 为什么使用交换? 2、为什么要创建temp变量而不是直接使用rhs? 如果您遵循代码,您应该从概念上了解为什么使用swap。所做的只是窃取temp 对象的内部结构并将temp 我们当前的内部结构提供给temp。当temp 最终消失时,它会带走旧的内容。其次,看看我的编辑——它基本上完成了同样的事情,唯一的区别是编译器在参数按值传递时为我们制作了临时副本。第一个版本具有自赋值测试,可能会加快速度,具体取决于编译器和类本身。 OK 听起来不错。我将阅读更多关于交换的内容,以了解为什么它比 = 运算符更好。关于第二个问题,通过编辑它更有意义。谢谢你的一切!【参考方案2】:

当你删除参数数组时,你需要提供一个 []

delete [] parameters;

否则您的程序中会出现未定义的行为。

旁注> 最好将其保留为 std::vector 或 std::array 。

【讨论】:

以上是关于在向量中使用擦除时双重释放或损坏(fasttop)。知道它们的索引,你怎么能擦除向量的几个项目?的主要内容,如果未能解决你的问题,请参考以下文章

从向量中擦除对象会导致双重释放[重复]

C++ 双重释放或损坏(出)

为啥使用 realloc() 时会出现双重释放或损坏错误?

作业帮助,分段错误,双重释放或损坏,free():无效指针

MySQL 重启使用 MySQL C API 导致双重释放或损坏

检测到 glibc - 在 C 程序中释放(int ** 类型)时出现双重释放或损坏消息