【问题标题】:What if I delete an array once in C++, but allocate it multiple times?如果我在 C++ 中删除了一次数组,但分配了多次怎么办?
【发布时间】:2015-05-20 14:11:26
【问题描述】:

假设我有以下 sn-p。

int main()
{
    int num;
    int* cost;
    while(cin >> num)
    {
        int sum = 0;
        if (num == 0)
          break;

        // Dynamically allocate the array and set to all zeros
        cost = new int [num];
        memset(cost, 0, num);
        for (int i = 0; i < num; i++)
        {
            cin >> cost[i];
            sum += cost[i];
        }
        cout << sum/num;
    }
`  `delete[] cost;
    return 0;
}

虽然我可以在 while 循环中移动 delete 语句 对于我的代码,为了理解目的,我想知道代码在编写时会发生什么。每次使用运算符new时,C++ 是否分配不同的内存空间?

operator delete 是否只删除 last 分配的 cost 数组?

【问题讨论】:

  • 要添加到答案中指出的错误,您还有一个比内存泄漏更糟糕的错误:如果您的 while 执行 0 次,那么您将有一个 delete[] 没有任何对应的new[],等等一个未初始化的指针。这是未定义的行为,实际上可能会使您的程序崩溃。 (好吧,在程序结束后发生一条指令可能不会那么戏剧化,但这不是一个漂亮的景象。)
  • 你不能多次分配一个数组。可以分配多个数组。
  • 您没有多次分配“the”数组。您正在分配“一个”数组。名称cost 是指针的名称,而不是数组的名称。

标签: c++ arrays memory memory-management new-operator


【解决方案1】:

每次我使用运算符new时,C++ 是否分配不同的内存空间?

是的。

operator delete 是否只删除 last 分配的 cost 数组?

是的。

您丢失了指向其他人的唯一指针,因此它们被不可挽回地泄露了。为了避免这个问题,不要玩弄指针,而是使用RAII 来自动管理动态资源。 std::vector 在这里是完美的(如果您真的需要一个数组;您的示例可以继续阅读并重复使用单个 int)。

【讨论】:

  • @doron:如果你小心它会正确删除数组,这是一个选择,尽管比vector 更奇怪。
  • 使用unique_ptr&lt;int[]&gt; cost;[] 表示一个数组。
  • 这正是为什么较新的语言只使用引用而不是指针的原因。当事情遵守 RAII 时,泄漏内存要困难得多......或者有一个实际的 GC 进程来清除内存中未引用的对象。
  • @sfdcfox 仅仅因为某些东西更万无一失并不一定意味着它更好
  • @sfdcfox:那个例子没有使用 RAII,所以它当然会泄漏。使用 RAII,即为 std::string a(5); a = std::string(10);string 类使用 RAII 自动释放内存,因此没有泄漏。答案的重点是说您应该使用 RAII,而不是手动指针杂耍,以避免泄漏。 GC 是 RAII 的替代品,但在标准 C++ 中不可用,所以我不知道你为什么要继续这样做。
【解决方案2】:

强烈建议你不要在C++程序中使用“C idioms”。让std 库为您工作:这就是它存在的原因。如果您想要“n 个整数的数组(向量)”,那么这就是 std::vector 的全部意义所在,并且它“包含电池”。 不必胡思乱想诸如“设置最大尺寸”或“将其设置为零”之类的事情。你只需使用“这个东西”,你不必[必须...]关心它的内部运作,因为它已经经过彻底的设计和测试。

此外,当您这样做时,您正在使用 C++ 现有的内存管理框架。特别是,您没有在您自己的应用程序中做任何“标准库不知道的,并且可能(!!)它的“带外”。 /p>

C++ 为您提供了一个非常全面的库,其中包含快速、高效、健壮且经过良好测试的功能。 利用它。

【讨论】:

    【解决方案3】:

    您的代码中没有 cost 数组。在您的代码中,cost 是一个指针,而不是一个数组。

    代码中的实际数组 是通过重复的new int [num] 调用创建的。每次调用 new 都会创建一个新的、独立的 nameless 数组对象,该对象位于动态内存中的某个位置。由new[] 创建的新数组可通过cost 指针访问。由于该数组是nameless,因此cost 指针是您拥有的唯一 链接,它指向new[] 创建的那个无名数组。您没有其他方法可以访问该无名数组。

    每次您在循环中执行 cost = new int [num] 时,都会创建一个全新的不同数组,断开从 cost 到前一个数组的链接,并使 cost 指向新数组。

    由于cost 是您到旧数组的唯一链接,因此旧数组将变得无法访问。对该旧阵列的访问将永远丢失。它变成了内存泄漏。

    正如您自己正确陈述的那样,您的 delete[] 表达式仅释放最后一个数组 - 最后一个 cost 最终指向。当然,只有当您的代码曾经执行过cost = new int [num] 行时,这才是正确的。请注意,您的循环可能会在不进行单次分配的情况下终止,在这种情况下,您会将 delete[] 应用于未初始化的(垃圾)指针。

    【讨论】:

      【解决方案4】:

      是的。因此,除了最后一次之外,循环的每次迭代都会出现内存泄漏。

      当你使用new 时,你分配了一块新的内存。将new 的结果分配给一个指针只会改变这个指针指向的内容。它不会自动释放该指针之前引用的内存(如果有的话)。

      【讨论】:

        【解决方案5】:

        首先这条线是错误的:

        memset(cost, 0, num);
        

        它假设一个 int 只有一个字符长。更典型的是四个。如果你想使用 memset 来初始化数组,你应该使用这样的东西:

        memset(cost, 0, num*sizeof(*cost));
        

        或者更好的是转储 memset 并在分配内存时使用它:

        cost = new int[num]();
        

        正如其他人指出的那样,delete 放置不正确,并且会泄漏由其对应的 new 分配的所有内存,除了最后一个。将其移入循环中。

        【讨论】:

        • 或者更好的是,使用std::vector
        • 是的,std::vector 会更好。
        【解决方案6】:

        每次为数组分配新内存时,之前分配的内存都会泄露。根据经验,您需要释放与分配的内存一样多的内存。

        【讨论】:

          猜你喜欢
          • 2021-10-15
          • 2020-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-08
          • 2020-07-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多