在对象数组上调用delete []无缘无故破坏堆

时间:2018-12-16 08:00:22

标签: c++ delete-operator heap-corruption

void Files::Push(const File& f)
{
    if(!s_)
    {
        files_ = new File[++s_];
        files_[0] = f;
        return;
    }

    File* tmp = new File[++s_];
    memcpy(tmp, files_, (s_-1) * sizeof(File));
    tmp[s_-1] = f;

    delete[] files_;

    files_ = tmp;
}

Full code is kind of big.但是对于这个特殊问题,它的代码非常简单,类非常简单。我真的看不到这里可能出什么问题。我不会两次调用delete[],不会调用delete而不是delete[],而是在保存new[]的结果的同一指针上调用它。我以为,也许其他一些代码在此特定的delete[]之前破坏了堆,但是我仔细检查了我的自定义字符串类(只有一个处理原始指针的类),并且可以100%正确地工作。

在完整代码中,您可以看到一些调试输出。尝试随机扫描文件夹中的第3个文件或第4个文件后崩溃。

D:\Dropbox\My Programs\FileSysScanner>fss
D:\Dropbox\My Programs\FileSysScanner\fss.exe
D:\Dropbox\My Programs\FileSysScanner\fss.exe Size: 45
D:\Dropbox\My Programs\FileSysScanner
fss.exe
Scanning...: D:\Dropbox\My Programs\FileSysScanner\*
File Found: fss.exe
1
2
Inside new pointer if... s: 0
3
4
Scanning...: fss.exe\*
FindFirstFileW() Failed! Error# 267
5
File Found: fss_first_prot.exe
1
2
push_1... s: 1
push_2... s: 2
push_3... s: 2
push_4... s: 2
push_5... s: 2
3
4
Scanning...: fss_first_prot.exe\*
FindFirstFileW() Failed! Error# 267
5
File Found: Main.cpp
1
2
push_1... s: 2
push_2... s: 3
push_3... s: 3
push_4... s: 3
<CRASH HERE>

下面描述的文件类。现在我在考虑将mb包含在File中不是最好的主意...也许这就是问题...

class File
{
public:
    File();
    File(const wchar_t* n, ulong b, bool isf, bool hid, bool sys);

    const Fname& Name() const;
    ulong Bytes() const;
    void AddBytes(ulong b);
    bool IsFolder() const;
    bool IsHidden() const;
    bool IsSystem() const;

    void PushFile(const wchar_t* n, ulong b, bool isf, bool hid, bool sys);
    void PushFile(const File& f);

private:
    void SetBytes(ulong b);
    ulong BytesTaken(ulong b) const;
    // Data
    Fname fn_;
    Files fs_;
    // Folder, Hidden and System attributes stored here in upper bits
    ulong bytes_;
    ulong bytes_taken_;
};

编辑:

我通过为文件以及对文件进行适当的深层复制构造函数和分配来修复了此问题。

Files::Files(const Files& other) : s_(other.s_)
{
    files_ = new File[s_];
    for(int i = 0; i < s_; ++i)
    {
        files_[i] = other.files_[i];
    }
}

Files& Files::operator=(const Files& other)
{
    if(this != &other)
    {
        File* tmp = files_;
        if(other.s_ != s_)
        {
            s_ = other.s_;
            tmp = new File[s_];
        }

        for(int i = 0; i < s_; ++i)
        {
            tmp[i] = other.files_[i];
        }

        delete[] files_;
        files_ = tmp;
    }

    return *this;
}

还使用简单循环而不是memcopy中的PushFile()

void Files::Push(const File& f)
{
    if(!s_)
    {
        files_ = new File[++s_];
        files_[0] = f;
        return;
    }
    File* tmp = new File[++s_];

    for(int i = 0; i < s_-1; ++i)
    {
        tmp[i] = files_[i];
    }

    tmp[s_-1] = f;

    delete[] files_;
    files_ = tmp;
}

现在,在文件中使用文件根本不是问题。与使用原始指针相同(当然,如果您知道自己在做什么)。如果我不尝试在非POD类上使用这种愚蠢的内存复制来简化事情,那么一切都会好起来的。

感谢帮助!

3 个答案:

答案 0 :(得分:3)

  

我的自定义字符串类(仅用于处理原始指针的类)

您的Files类具有原始指针,因此存在bug。您的File类包含一个Files对象,因此也是错误的。

在非POD类型(例如memcpy)上的

File是另一个错误。

不要使用原始指针,或者如果您坚持要确保您的类具有正确的复制语义。不要存储对象,使用复制构造函数和赋值。编写代码的简单有效方式是使用std::wstringstd::vector

答案 1 :(得分:3)

在高级上下文中过多使用了不合适的低级函数,以使此代码始终可靠。这是我看到的最大问题:

memcpy(tmp, files_, (s_-1) * sizeof(File));

您不能以这种方式使用memcpy。一方面,您的File类中包含一个Files元素。您的Files类中有一个File*。当您在memcpy上执行File时,其地址会更改,从而破坏了所有存放原始指针的地方。

请尽可能使用值代替指针。值易于存储和复制,不必删除,也不能悬挂。随意使用原始指针会失去所有这些好处。

答案 2 :(得分:2)

请注意两件事:

  • delete[]为所有要删除的数组元素调用析构函数;
  • memcpy仅复制字节,而无需处理需要使用复制构造函数复制的内容。

如果您的File类具有除基本类型以外的其他内容,例如指针,您会遇到麻烦,因为memcpy时,您没有正确复制这些成员。当您delete[]原始数组时,将为已删除元素的成员调用析构函数,从而使您的tmp副本具有无效值。

最好不要将memcpy用于基本类型(例如整数)以外的任何其他类型。使用std::vector存储File对象,并使用push_back向向量添加新项目。