C++错误:双重释放或损坏(fasttop)

3

我想知道为什么当我运行以下程序时会出现“double free or corruption (fasttop)”的错误。我知道我可以使用字符串而不是字符数组。但我想使用带有动态内存分配的字符数组。请问如何解决这个问题?

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

class Cube
{
public:
    char *str;

    Cube(int len)
    {
        str = new char[len+1];
    }

    Cube(const Cube &c)
    {
        str = new char[strlen(c.str) + 1];
        strcpy(str, c.str);
    }   
    ~Cube()
    {
        delete [] str;
    }
};

int main()
{
    vector <Cube> vec;

    for (int i = 0; i < 10; i++)
    {
        char in [] = "hello !!";
        Cube c(strlen(in)+1);
        strcpy(c.str, in);
        vec.push_back(c);
    } 

    int i = 0;
    for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); )
    {
        cout << it->str << endl;
        i++;
        if (i % 2 == 0)
            it = vec.erase(it);
        else
            it++;
    }


    for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); it++)
    {
        cout << it->str << endl;
    }
    return 0;    
}

当我在Windows上的MinGW编译和运行此代码时,我没有遇到任何错误。你使用的是哪个编译器? - David Grayson
我在Ubuntu上使用g++。 我已经重载了赋值运算符。现在它可以正常工作。 - miraj
好的。是的,当我运行程序时,我得到了损坏的数据,但添加赋值运算符似乎解决了这个问题。 - David Grayson
3个回答

13

您忘记定义类的operator=。这是大三法则的一部分(复制构造函数,析构函数和赋值操作符必须全部定义)。


从c++references:在向量的末尾添加一个新元素,该元素位于其当前最后一个元素之后。此新元素的内容将初始化为x的副本。 - Miguel Angel
太棒了!!我已经实现了赋值运算符(operator=),它完美地工作了。非常感谢!!! - miraj

6

n.m.已经给出了不错的答案,但我觉得这个问题很有意思,所以决定进一步理解它。

原来当你在迭代器的第一个项目(我们称其为item0)上调用erase()时,迭代器会执行以下操作:使用您类的=运算符执行item0 = item1。然后删除item1

如果没有定义自己的=运算符,我想它将简单地复制对象的内存从item1item0,所以item0item1将暂时指向同一个字符串。然后当item1被删除时,该字符串被释放,留下item0处于无效状态,因为它具有指向已释放内存的指针。

这是一些简单的测试代码,可重现并阐明问题:

#include <cstring>
#include <vector>
#include <stdio.h>
using namespace std;

class Cube
{
public:
    char * str;

    Cube(const Cube &c) { set(c.str); }
    Cube(const char * s) { set(s); }
    ~Cube() { clear(); }  // is "delete []" necessary?  not sure

#if 1    // change to 0 to cause a bug
    void operator=(const Cube &c)
    {
        clear();   // necessary to avoid memory leaks
        printf("operator=\n");
        set(c.str);
    }
#endif

private:
    void set(const char * s)
    {
        str = new char[strlen(s) + 1];
        printf("allocated %p for %s\n", str, s);
        strcpy(str, s);
    }

    void clear()
    {
        if (str)
        {
             printf("freeing %p: %s\n", str, str);
             delete str;
        }
    }
};

int main(int argc, char ** argv)
{
    printf("== CREATING VECTOR ==\n");
    vector <Cube> vec;
    vec.push_back(Cube("octopus"));
    vec.push_back(Cube("squid"));

    printf("== BEGINNING ITERATION ==\n");
    vector<Cube>::iterator it = vec.begin();
    printf("First entry is %p %s\n", it->str, it->str);
    it = vec.erase(it);
    printf("Second entry is %p %s\n", it->str, it->str);  // this prints garbage if Cube has no = operator
    return 0;    
}

这段代码会产生以下输出:
== CREATING VECTOR ==
allocated 00350F98 for octopus
allocated 00350FB8 for octopus
freeing 00350F98: octopus
allocated 00350F98 for squid
allocated 00350FD8 for squid
allocated 00350FE8 for octopus
freeing 00350FB8: octopus
freeing 00350F98: squid
== BEGINNING ITERATION ==
First entry is 00350FE8 octopus
freeing 00350FE8: octopus
operator=
allocated 00350F98 for squid
freeing 00350FD8: squid
Second entry is 00350F98 squid
freeing 00350F98: squid

我使用MinGW在Windows上编译并运行了这个程序。我使用的命令是g++ -Wl,--enable-auto-import test.cpp && a.exe


0

如果疼痛,请不要做:

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

class Cube
{
public:
    string str;

    Cube(const string& s) : str(s) { }
};

int main()
{
    vector <Cube> vec;

    for (int i = 0; i < 10; i++)
    {
        char in [] = "hello !!";
        vec.push_back(Cube(in));
    } 

    int i = 0;
    for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); )
    {
        cout << it->str << endl;
        i++;
        if (i % 2 == 0)
            it = vec.erase(it);
        else
            it++;
    }


    for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); it++)
    {
        cout << it->str << endl;
    }
    return 0;    
}

恰好更短且正确(未经测试)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接