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

Calling delete[] on array of objects corrupts heap for no reason

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. 但是对于这个特定的问题,它的代码非常简单,非常简单 classes。我真的看不出这里出了什么问题。我不调用 delete[] 两次,我不调用 delete 而不是 delete[],我在保存 new[] 结果的同一个指针上调用它。我想,也许其他一些代码在这个特定的 delete[] 之前破坏了堆,但我仔细检查了我的自定义字符串 class(只有一个处理原始指针的代码)并且它 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>

文件 class 如下所示。现在我认为 mb 包括文件中的文件不是最好的主意...也许这就是问题所在...

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 和 File 制作适当的深层复制构造函数和赋值来修复它。

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;
}

并且还在 PushFile() 中使用简单循环代替 memcopy:

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 classes 上使用这种愚蠢的 memcopy 来过度简化事情,一切都会好起来的...

感谢帮助!

请注意两点:

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

如果您的 File class 有原始类型以外的东西,例如指针,你有麻烦了,因为当你 memcpy 时,你没有正确地复制那些成员。当您 delete[] 原始数组时,会为已删除元素的成员调用析构函数,这会使您的 tmp 副本具有无效值。

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

my custom string class (only one that deals with raw pointers)

您的 Files class 有一个原始指针,因此存在错误。您的 File class 包含一个 Files 对象,所以这也是错误的。

memcpy 在非 POD 类型(例如 File)上是另一个错误。

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

在高级上下文中使用了太多不适当的低级函数,无法使此代码可靠。这是我看到的最大问题:

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

你不能这样使用memcpy。一方面,您的 File class 中有一个 Files 元素。您的 Files class 中有一个 File*。当您在 File 上执行 memcpy 时,它的地址会发生变化,从而破坏您存储指向它的原始指针的所有位置。

尽可能使用值而不是指针。值易于存储和复制,不必删除,也不会悬空。无故使用原始指针会失去所有这些好处。