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

Posted

技术标签:

【中文标题】在 C++ 循环中正确删除动态分配的对象的位置【英文标题】:Where to properly delete dynamically allocated object in loop in C++ 【发布时间】:2013-10-30 21:40:27 【问题描述】:

教程、搜索和我的 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] = '\0'


T::~T() 
    delete [] strVar;


// Foo.cpp
T * Foo::getNewT() 
    T * t = new T;

    // populate T's fields

    return t;

分辨率:

因为仅使用T * t 进行的简单测试并且循环工作正常,我最终从空白开始重建项目并一次添加一个类,以查看问题何时出现。原来我在程序的其他地方添加了额外的内容到动态分配的数组中,而没有更新我用来初始化数组的大小常量。

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

【问题讨论】:

带有预处理语句? 如果您需要帮助确定实际导致崩溃的原因,我建议您发布getNewT()T()~T() 的代码,并可能显示T 的类声明。 【参考方案1】:

假设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());

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

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

在您的编辑中添加其他信息:

char * strVar = new char[11];

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

【讨论】:

你提到了一些很好的一般问题;我不相信他们会成为这个项目的问题,但我会记住他们。 (foo 不保留对新 T 的任何引用,并且不会在其 T 的上下文之外引用 T.strVar。我的一些指针用法,例如 strVar,是因为我对 C 比对 C++ 更舒服) “最终的t”也可以是nullptrdelete-ing 不也是 UB 吗? 删除空指针什么都不做。拥有T* p = nullptr; delete p; 是完全可以接受的。 ***.com/questions/4190703/…【参考方案2】:

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

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

【讨论】:

+1(我怀疑最后一个假设不会成立,这可以解释 OP 在平台切换行为中的不一致结果)。【参考方案3】:

当您不再需要动态分配的内存时,您应该删除它。如果您希望t 将其值保留在for 循环内,则在循环外将其删除,否则在循环内将其删除。

然而,最好的办法是当你真的必须使用指针时使用std::unique_ptr。当所有对内存的引用都被销毁时,它将负责释放内存本身。您应该尽量避免分配内存。如果适合工作,请使用 STL 容器。

【讨论】:

【参考方案4】:

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

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

【讨论】:

以上是关于在 C++ 循环中正确删除动态分配的对象的位置的主要内容,如果未能解决你的问题,请参考以下文章

在删除指向动态分配对象的指针向量中的元素之前,我需要做啥?

如何在 C++ 中正确使用动态分配的多维数组 [重复]

获取动态分配的数组大小

动态分配的双向链表类实例 segfault C++

删除动态分配的二维数组时发生错误?

在 C++ 中的 2D 动态内存分配数组中释放分配的内存