【问题标题】:C++: ways to free a dynamic array (member of a struct) and a pointer to this structC ++:释放动态数组(结构成员)和指向该结构的指针的方法
【发布时间】:2018-04-07 20:18:20
【问题描述】:

全部。 假设我们有一个指向结构的指针,该结构的成员是一个动态数组(以及其他成员)。

我可以释放所有对象,但希望您对针对这种特定情况的最佳做法提出意见。请参阅下面的代码,它可以编译并运行而不会出现分段错误:

#include <iostream>

struct TestStruct
    {
    int a;  // And other members here
    int *b = new int[10];

    ~TestStruct()
        {
        }
    };

int main(void)
    {
    struct TestStruct *a_struct = new TestStruct();

    // Do something with the struct

    delete[] a_struct->b;
    delete a_struct;

    return 0;
    }

这样我假设内存被正确返回。但是,如果我将这些删除中的任何一个移动到析构函数中,就会出现段错误。也就是说,如果我将数组删除移动到析构函数(delete[] a_struct-&gt;b;),它不再可访问,因为我之前删除了指向该结构的指针(delete a_struct;),反之亦然,就会发生内存泄漏。

在阅读了这个帖子C++ free all memory used by struct之后,有点不确定,因为大多数建议都被认为是理所当然的工作,但其中许多存在segfault。

我已经简化了问题,因为我将使用的数组是 3D。如果无法在析构函数中释放 100% 的内存,那么我准备使用一种方法来运行循环以释放数组内存和指向结构的指针。所以我想知道你对这种特殊情况的看法。

【问题讨论】:

  • 使用std::arraystd::vector

标签: c++ arrays memory struct raii


【解决方案1】:

由于您使用的是 C++ 和动态数组,std::vectorstd::array 或例如 std::unique_ptr 都是比直接使用 new 更好的处理方法。

【讨论】:

  • 谢谢。我曾想过使用安全数组,但我将在一个巨大的 3D 数组上执行一个非常长的循环,并且使用这些容器的访问方法来写入和检索单个元素会增加我无法承受的性能损失。我以前试过。
  • @JayY 使用std::vectorstd::array 不会影响性能,它们基本上是原生数组周围的语法糖
  • 我不认为 std::vector/std::array 比你激活编译器优化的 c 样式数组慢得多。
  • 但在数亿次迭代中,累积的开销是相当可观的。我正在处理大量的地震数据。我试过了。
  • @JayY 你测量了吗?不应该有任何开销。访问std::vector 元素生成的代码应该与访问本机数组生成的代码没有区别。
【解决方案2】:

正确的 RAII 方法是:

struct TestStruct
{
    int a;  // And other members here
    int *b = new int[10];

    ~TestStruct()
    {
            delete[] b;
    }
};

int main(void)
{
    struct TestStruct *a_struct = new TestStruct();

    delete a_struct;

    return 0;
}

正确的设计不允许通过同一个指针字段进行多次删除。如果存在这种风险,您可以将nullptr 分配给指针。删除空指针是noop。

RAII(资源获取即初始化)本质上归结为:谁分配了内存,谁就释放了。

【讨论】:

  • 感谢您的回复。然而,正如我之前提到的,当我们在析构函数中尝试 delete[] b 时,这样做会导致段错误,因为它不再可访问,因为 delete a_struct 已经删除了对它的引用。
  • 作为此答案的一部分发布的代码是正确的 AFAICT,并且在我的机器(MacOS/X、clang++)上运行它不会产生运行时错误。在 C++ 中,TestStruct 析构函数在 回收 TestStruct 的内存之前执行,否则会导致析构函数无法释放任何资源。我认为,如果您遇到段错误,则一定有其他原因。
  • @JayY 唯一可能出现段错误的原因是堆之前以某种方式损坏了。您没有发布失败的代码,因此问题不可重现
  • @Swift 你建议的这个代码,我在这里问问题之前尝试过,会导致段错误。我尝试了几种不同的编译器(g++、MSVC)。在我看来,正在发生的事情是:1)程序释放指针 a_struct 的内存; 2) 调用析构函数; 3) 程序尝试释放数组a_struct->b的内存,但不再有a_struct的引用; 4) 段错误。
  • @JayY 你的意思是 mmy 程序错误的精确副本?我可以证明至少有十几个编译器不这样做,并且您描述的顺序与 c++ 语言的规则不匹配。见这里:ideone.com/jRowfe
【解决方案3】:

在析构函数中,只删除动态分配的成员,而不是对象本身(这是在销毁过程中完成的)。

所以下面的代码应该没问题:

struct TestStruct
{
  int a;  // And other members here
  int *b = new int[10];

  ~TestStruct() {
     delete[] b;
  }
};

int main(void)
{
  struct TestStruct *a_struct = new TestStruct();

  // Do something with the struct

  delete a_struct;

  return 0;

}

【讨论】:

  • 大声笑,在同一秒钟内......同样,从技术上讲你可以delete this,但如果它是从析构函数完成的,它会导致析构函数的递归调用。
  • @Stephan,谢谢,但这是我在上面对 Swift 的评论中提到的问题。在问这个问题之前我已经尝试过了。删除 a_struct 后,析构函数中的 delete[] b 会导致段错误(a_struct 不再可访问)。
  • @JayY: delete a_struct 实际上触发了析构函数,在析构函数中,this 始终是可访问的(否则任何析构函数代码都没有多大意义)。你确定你删除了main中的delete[] a_struct-&gt;b吗?
  • @StephanLechner 是的,我以前试过这个,但是如果我已经删除了 main() 中的某些内容,它会在析构函数中的任何删除命令上出现段错误。我已经为人们在这里提出的任何建议准备好了编译器。
  • @JayY 我不明白为什么发布的代码应该出现段错误。你用完全那个代码试过了吗?
猜你喜欢
  • 2017-07-27
  • 2012-07-16
  • 1970-01-01
  • 2023-03-12
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-02
相关资源
最近更新 更多