双重删除数据不会崩溃

时间:2014-02-18 09:28:21

标签: c++ memory

我正在尝试学习C ++,并且正在编写程序来学习复制构造函数和运算符重载。我很惊讶使用Copy构造函数时下面的程序不会崩溃说“Double Free”,而使用Operator = overloading时会一直崩溃。

#include <iostream>
using namespace std;

class XHandler
{
    public:
    XHandler()
    {
            data = new char[8];
            strcpy(data, "NoName");
    }
    XHandler (const char *str)
    {
            data = new char (strlen(str) + 1 );
            strcpy (data, str);
    }
    XHandler (const XHandler &xh)
    {
            data = xh.data;
    }
    XHandler& operator = (const XHandler &xh)
    {
            data = xh.data;
    }
    ~XHandler()
    {
            delete data;
    }
    void debug()
    {
            cout << data <<endl;
    }
    private:
    char *data;
};

int main()
{
    XHandler wm("hello"), wb("there");
    wm.debug();
    wb.debug();
    XHandler wc (wm);
    wc.debug();
    XHandler wd;
    wd = wc;
    wd.debug();
}

请注意,在复制构造函数和运算符重载中,我只是将'data'指针从一个对象复制到另一个对象。当为'wd'和'wc'调用析构函数时,它会一致地崩溃程序。如果我评论下面的行并只执行复制构造函数,程序根本不会崩溃。我希望既然'wc'和'wm的数据变量也指向同一个指针,程序就会崩溃。

    XHandler wd;
    wd = wc;
    wd.debug();

我理解双删除是未定义的行为。但是,我想知道它是一直以某种方式崩溃而不是另一种方式崩溃。

3 个答案:

答案 0 :(得分:5)

“未定义的行为”的行为是未定义的。这意味着,没有人为这种情况付出努力,因为这种情况不应该发生。即使是工作程序也可能是“未定义的行为”。

在双重删除的特殊情况下,会发生什么,实际上取决于分配器的实现。通常的实现是将释放的内存放入列表中,并且下一个分配将满足该列表中的元素。如果双重删除一块内存,它将被添加到列表中两次。然后两个分配将满足相同的内存块,因此将在同一个内存位置创建两个对象。

BTW:你的析构函数已经坏了,因为它没有使用数组删除。

答案 1 :(得分:2)

我支持这段测试代码,希望它能在这里提供帮助,我认为暗恋很多都与平台有关,它不仅仅是偶然的,因为这段代码粉碎在linux中,但在codeblock IDE中工作正常< / p>

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

class XHandler
{
    public:
    XHandler()
    {
            data = new char[8];
            strcpy(data, "NoName");
            std::cout<< "default construcor is called" << std::endl;
    }
    XHandler (const char *str)
    {
            data = new char [strlen(str) + 1 ];
            strcpy (data, str);
             std::cout<< "param construcor is called" << std::endl;

    }
    XHandler (const XHandler &xh)
    {
            data = xh.data;
            std::cout<< "copy construcor is called" << std::endl;

    }
    XHandler& operator = (const XHandler &xh)
    {
            data = xh.data;
            std::cout<< "operator construcor is called" << std::endl;

            return *this;
    }
    ~XHandler()
    {
        std::cout<< "destrucor is called" << std::endl;
        print_dir();
        if (data)
        {
            delete [] data;
            data = NULL;
            std::cout<< "delete data" << std::endl;
        }



    }
    void debug()
    {
            cout << data <<endl;
    }
    void print_dir()
    {
        printf("location: %p\n",data);
    }
    private:
    char *data;
};

int main()
{
    XHandler wm("hello"), wb("there");
    wm.debug();
    wb.debug();
    wm.print_dir();
    wb.print_dir();
    XHandler wc (wm);
    wc.print_dir();
    wc.debug();
    XHandler wd;
    wd = wc;
    wd.debug();
}

它在linux gcc4.1.2中粉碎,输出

param construcor is called
param construcor is called
hello
there
location: 0x502010
location: 0x502030
copy construcor is called
location: 0x502010
hello
default construcor is called
operator construcor is called
hello
destrucor is called
location: 0x502010
delete data
destrucor is called
location: 0x502010
*** glibc detected *** ./test: double free or corruption (fasttop): 0x0000000000502010 ***

答案 2 :(得分:0)

您的程序有不确定的行为。

问题:

data = new char (strlen(str) + 1 );

这不会分配strlen(str) + 1个字符。相反,它会分配一个字符并将strlen(str) + 1值放入其中。当你之后执行strcpy时,你通过缓冲区溢出来破坏堆栈(写入strlen(str)-1 chars超过分配的空间)。要分配数组,您应该使用方括号:

data = new char [strlen(str) + 1 ];

其次,您应该使用delete[] data;删除数据。否则,您会得到未定义的行为。

第三,strcpy代码不会复制字符串的NULL终止符。您应该添加一个空终止符:

data[ strlen(str) ] = 0;
在strcpy行之后

; (见评论)。

此外,不要求应用程序在双重删除时崩溃。两次删除相同的内存是UB。