在C++循环中正确删除动态分配对象的位置

Where to properly delete dynamically allocated object in loop in C++

本文关键字:动态分配 对象 位置 删除 C++ 循环      更新时间:2023-10-16

教程、搜索和对我C++正规教育的模糊记忆让我不知道在循环中使用动态分配的对象指针时应该在哪里使用 delete,例如:

// necessary files are included, this code is within main
T * t;
t = foo.getNewT();
while (!t->isFinalT()) {
    // print t stuff
    delete t;         // is this where I should delete t?
    t = foo.getNewT();
}
delete t;

这种知识的缺乏在最近的一个课堂项目中变得特别麻烦。在我的笔记本电脑(Linux Mint,g++ Ubuntu/Linaro 4.7.3-1ubuntu1)上,代码在没有删除语句的情况下运行良好,并且在我添加删除语句时崩溃。在学校服务器(Solaris,g++ (GCC) 3.4.5)上,代码在没有删除语句的情况下进行了几次迭代后出现段错误,并且在我添加删除语句时运行良好。

如何正确处理这种循环,使其在大多数环境中运行?

附加信息:当程序到达删除请求时,我的笔记本电脑上会出现错误:

*** Error in 'program': free(): invalid next size (fast):......

其他一些代码:

// T.h
class T {
    int id;
    int num;
    int strVarPos;
    char * strVar;
  public:
    T();
    ~T();
    // + misc. methods
}
// T.cpp
T::T() {
    id = 0;
    num = -1;
    strVarPos = 0;
    char * strVar = new char[11];
    strVar[0] = ''
}
T::~T() {
    delete [] strVar;
}
// Foo.cpp
T * Foo::getNewT() {
    T * t = new T;
    // populate T's fields
    return t;
}

分辨率:

因为只有T * t的简单测试并且循环工作正常,所以我最终从空白开始重建项目并一次添加一个类,以查看问题何时会出现。事实证明,我已经将其他内容添加到程序其他地方的动态分配数组中,而没有更新我用于初始化数组的大小常量。

显然,如果我确保正确删除指针,学校服务器只能处理由此产生的内存差异而不会崩溃(程序运行时间不够长,无法在我的测试中导致严重的内存泄漏),而我的笔记本电脑不会注意到内存差异,直到我尝试调用删除(然后会崩溃)。

假设foo.getNewT()将内存的所有权移交给调用方:

T * t;
t = foo.getNewT();
//while (!t->isFinalT()) // if foo.getNewT ever returns NULL, this will be UB!!!
while (t != nullptr && !t->isFinalT()) 
{
    // ...
    delete t; // if you now own it and are no longer going to use it, yes, delete it here
    t = foo.getNewT();
}
delete t; // you also need this one to delete the "final" t

但是,您可以使用std::unique_ptr来避免自己做:

std::unique_ptr<T> t;
t.reset(foo.getNewT());
while (t && !t->isFinalT()) 
{
    // ...
    t.reset(foo.getNewT());
}

或者,您可以重写循环以更好地流动:

std::unique_ptr<T> t;
do
{
    t.reset(foo.getNewT());
    if (t)
    {
        // do stuff with t
    }
} while (t && !t->isFinalT());

代码在没有 delete 语句的情况下运行良好,并在我崩溃时崩溃 添加了删除语句。

确定getNewTT*的所有权交给您吗? 如果您删除它,然后它稍后尝试删除它,您最终将导致堆损坏。 如果它将所有权移交给调用方,并且您不删除它,则会发生内存泄漏。

使用编辑中的其他信息:

char * strVar = new char[11];

如果将strVar声明为 std::stringchar[11],则不需要该行。 如果尝试复制其中任何T对象,则将使用默认的复制构造函数(因为尚未定义一个),它将执行浅表复制(即,复制指针的值以进行strVar)。 当您删除 2 个都指向同一内存位置的 T 时,会出现堆损坏。 最可靠的解决方案是将strVar声明为std::string

问题不在于delete。 你把它放在正确的地方。 更有可能是你正在做的其他事情导致了不确定的行为。

请注意,在循环之后也应该有一个delete t(以捕获最后一个)。 这是假设foo.getNewT()总是返回一个有效的指针(它必须返回,因为你从不检查它是否为 NULL)。

当您

不再需要动态分配的内存时,应将其删除。如果您希望tfor循环中保存其值,请在循环外部删除它,否则将其删除。

但是,最好的办法是在真正必须使用指针时使用 std::unique_ptr .当对内存的所有引用都被销毁时,它将负责解除分配内存本身。您应该尽量避免分配内存。如果 STL 容器适合作业,请使用它们。

我认为当你delete t时,你正在删除结构内的真实对象。

也许这就是导致问题的原因。