【问题标题】:Does this C++ Class Leak Memory?这个 C++ 类是否泄漏内存?
【发布时间】:2018-09-16 03:50:47
【问题描述】:

我想知道我为程序创建的类是否会泄漏内存。

这是我的实现,

BubbleSort.h 有 3 个私有变量:

int arrSize = Array Size
int* intArray = Original Array
int* narr = Bubble Sorted Array

BubbleSort.cpp

#include "BubbleSort.h"

BubbleSort::BubbleSort(int size, int* arr)
{
    arrSize = size;
    intArray = arr;
    printf("BubbleSort();\n");
}


// Object Destruction
BubbleSort::~BubbleSort()
{
    free(narr);
}

int* BubbleSort::Sort() {
    int swap = 0;
    narr = (int*)malloc(arrSize * sizeof(int));

    for (int i = 0; i < arrSize; i++) {
        narr[i] = intArray[i];
    }

    for (int i = 0; i < arrSize - 1; i++) {
        for (int j = (i + 1); j < arrSize; j++) {
            if (narr[j] < narr[i]) {
                swap = narr[i];
                narr[i] = narr[j];
                narr[j] = swap;
            }
        }
    }
    return narr;
}

这是我的主文件

#include "main.h"
#include "classes.h"

int main(int argc, char* argv[]) {
    const int size = 10;
    int arr[size] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};

    BubbleSort sort = BubbleSort::BubbleSort(size, arr);

    int* narr = sort.Sort();

    for (int i = 0; i < size; i++) {
        printf("%d > %d\n", arr[i], narr[i]);
    }

    printf("Hello World.\n");
    getchar();
}

这个程序在运行过程中会不会泄露内存?

当我运行代码时,我检查了 BubbleSort 实例“排序”是否正在破坏,但那只是在应用程序完成运行时。我目前正在学习如何在我的编程课程中使用malloc() 和free() 并且有一些空闲时间,所以我决定制作一个冒泡排序课程并想尝试一下!所以希望代码不要有太多错误(与指针的使用有关)。

【问题讨论】:

  • 如果您排序不止一次,就会出现泄漏。注意:您应该能够在不分配更多内存的情况下进行排序。如果您需要保留原件,请将其复制并排序,因为您将获得更通用的排序方法。注意:这是 C++。所以你不应该在一些边缘情况之外使用malloc/free。
  • 除了泄漏之外,构造然后立即销毁类的实例将导致尝试释放未分配的内存、未定义的行为以及几乎可以保证的崩溃。
  • 真的没必要写一个类来做冒泡排序。一个简单的功能就足够了。通常,当您需要在程序生命周期的某些部分维护状态时使用类。
  • 当您更多地思考什么是内存泄漏(整个程序的全局错误功能)时,您就会明白您的问题毫无意义:一个类本身不能泄漏,只有使用它的整个程序可能(或不)泄漏。内存管理不是本地或模块化功能(这就是它如此困难的原因)。像valgrind 这样的工具可以提供帮助,因为它们可以观察整个程序的行为

标签: c++ memory-management memory-leaks


【解决方案1】:

除非与 C 代码交互,否则不应使用 malloc 和 free。尽量避免使用new 和delete,因为C++ 有许多更简单的模式可以保证不会泄漏并且速度一样快。想想std::vector,如果vector不够好,想想std::unique_ptr&lt;int[]&gt; narr和std::make_unique&lt;int[]&gt;(size)。

您的程序不会泄漏,也不会因侥幸而崩溃。为了避免泄漏,你的类应该有一个关于内存的明确不变量,它没有。无法知道narr 何时具有无效指针,或者何时指向必须释放的内存。结果导致以下泄漏:

int arr1[size] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
BubbleSort sort = BubbleSort::BubbleSort(size, arr1);
int* narr = sort.Sort();
narr = sort.Sort();

以下内容具有未定义的行为(通过未初始化的指针释放):

int arr1[size] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
BubbleSort sort = BubbleSort::BubbleSort(size, arr1);
return 0;

处理此问题的最简单方法(在 99.9% 的情况下是正确的)是定义所有权。您预先决定谁拥有什么对象。使用std::vector 和std::unique_ptr,语言会为您决定。该语言不允许您在没有明确操作的情况下意外转移std::unique_ptr 的所有权。如果你犯了错误,编译器只会给你一个错误。


附注:如果你想学习 C++,如果你有 C 背景,从malloc、free、printf 开始可能更容易,到处都是原始指针。但是,您应该记住,这不是惯用的 C++。迟早你必须转向像 RAII 这样的 C++ 习语,具有明确所有权和不变性的类,并使用 C++ 库而不是 C。

【讨论】:

  • 对于原语使用 malloc 和 delete 并没有实际的缺点,如果使用 new 则没有 eqv 来重新分配。当它有意义时,我给自己写了一个 RAII 包装器。
  • @joshua,你的意思是 malloc 和免费的。缺点是代码会不断发展,最终它不会是原语。然后构造函数/析构函数将不会被 malloc/free 调用,您必须使用带有 unique_ptr/shared_ptr 的删除器,或者自己编写。没什么太严重的,但只是另一个可能引入严重错误的地方。概率方面,如果你有很多这样的地方,那么你的代码中就会出现与内存相关的错误,这是可以避免的。无论如何,我认为 c++ 可以从 renew 运算符中受益,但很难在不破坏现有代码的情况下定义一个。
  • 您想要续订到什么程度?我认为它可以作为一个库函数。
  • @Joshua 要使更新可行,它必须合并到分配器和容器中(因此向量将使用它)。它还必须是一个运算符,以便在内存移动时调用复制构造函数/析构函数。它还必须调用默认构造函数/析构函数来解释分配大小的变化。可移植库无法知道内存块的当前大小。
  • 模板方法可以调用必要的构造函数和析构函数。分配器比库方法的实际实现更便宜。唯一的可移植性问题是 sbrk 不再可移植。 (DOS 提供了它;Windows 没有。)
【解决方案2】:

这种分配模式非常糟糕。是的,您提供的程序不会泄漏,但如果您不重新考虑您的模式,您将在更大的程序中泄漏。

要么只在构造函数(RAII)中分配narr,要么不在析构函数中释放,而是在调用函数(老派)中释放,或者完全摆脱对象并将size和arr传递给@ 987654324@.

很快开始使用标准模板库将是明智之举,但这是另一天的话题。

正如 Sam Varshavick 指出的那样,如果您创建其中一个 is 并且不调用 sort 您会在析构函数中崩溃。我提供的任何方法都可以解决这个问题,在构造函数中将narr 初始化为NULL。

【讨论】:

    猜你喜欢
    • 2016-09-20
    • 2014-11-17
    • 1970-01-01
    • 2010-10-19
    • 2022-11-04
    • 1970-01-01
    • 2011-02-20
    • 2013-08-12
    相关资源
    最近更新 更多