【问题标题】:Dedicated function for memory allocation causes memory leak?内存分配的专用函数导致内存泄漏?
【发布时间】:2009-04-08 17:07:10
【问题描述】:

大家好,

我相信下面这段代码会产生内存泄漏?

    /* External function to dynamically allocate a vector */
    template <class T>
            T *dvector(int n){
            T *v;

            v = (T *)malloc(n*sizeof(T));

            return v;
    }


    /* Function that calls DVECTOR and, after computation, frees it */
    void DiscontinuousGalerkin_Domain::computeFaceInviscidFluxes(){
            int e,f,n,p;
            double *Left_Conserved;

            Left_Conserved = dvector<double>(NumberOfProperties);

            //do stuff with Left_Conserved
            //

            free(Left_Conserved);

            return;
    }

我认为,通过将指针传递给 DVECTOR,它会分配它并返回正确的地址,以便 free(Left_Conserved) 成功解除分配。但是,似乎并非如此。

注意:我还测试了 new/delete 替换 malloc/free 也没有成功。

我有一段用于分配二维数组的类似代码。我决定像这样管理向量/数组,因为我经常使用它们,而且我还想更深入地了解 C++ 的内存管理。

所以,我很想保留一个外部函数来为我分配向量和数组。有什么办法可以避免内存泄漏?

编辑

我也一直在使用 DVECTOR 函数来分配用户定义的类型,所以我猜这可能是个问题,因为我没有调用构造函数。

即使在我释放 Left_Conserved 向量之前的代码段中,我也想以其他方式分配一个向量并将其“打开”以供其他函数通过其指针进行评估。如果使用 BOOST,它会在函数结束时自动清理分配,所以,我不会用 BOOST 获得“公共”数组,对吧?我想这很容易用 NEW 解决,但是对于矩阵来说,更好的方法是什么?

我刚刚想到我将指针作为参数传递给其他函数。现在,BOOST 似乎不太喜欢它,编译退出并出现错误。

所以,我仍然需要一个指向向量或矩阵的指针,它接受用户定义的类型,将作为参数传递给其他函数。向量(或矩阵)很可能在外部函数中分配,并在另一个合适的函数中释放。 (我只是不想复制循环和新的东西来在代码中的任何地方分配矩阵!)

这是我想做的:

    template <class T>
    T **dmatrix(int m, int n){
            T **A;

            A = (T **)malloc(m*sizeof(T *));
            A[0] = (T *)malloc(m*n*sizeof(T));

            for(int i=1;i<m;i++){
                    A[i] = A[i-1]+n;
            }

            return A;
    }


    void Element::setElement(int Ptot, int Qtot){

            double **MassMatrix;

            MassMatrix = dmatrix<myT>(Ptot,Qtot);

            FillInTheMatrix(MassMatrix);

            return;
    }

【问题讨论】:

    标签: c++ memory-leaks


    【解决方案1】:

    那里没有内存泄漏,但您应该使用 new/delete[] 而不是 malloc/free。特别是因为您的函数是模板化的。

    如果您想使用具有非平凡构造函数的类型,则基于 malloc 的函数将被破坏,因为它不调用任何构造函数。

    我会用简单的方法替换“dvector”:

    void DiscontinuousGalerkin_Domain::computeFaceInviscidFluxes(){
            double *Left_Conserved = new double[NumberOfProperties];
    
            //do stuff with Left_Conserved
            //
    
            delete[] Left_Conserved;
    }
    

    它在功能上是等价的(除了它可以调用其他类型的构造函数)。它更简单,需要更少的代码。另外,每个 c++ 程序员都会立即知道发生了什么,因为它不涉及额外的功能。

    更好的是,使用智能指针来完全避免内存泄漏:

    void DiscontinuousGalerkin_Domain::computeFaceInviscidFluxes(){
            boost::scoped_array<double> Left_Conserved(new double[NumberOfProperties]);
    
            //do stuff with Left_Conserved
            //
    }
    

    正如许多聪明的程序员喜欢说的“最好的代码是你不必编写的代码”

    编辑:您为什么认为您发布的代码会泄漏内存?

    编辑:我看到你对另一篇帖子的评论说

    在代码执行命令顶部显示 分配的内存增长 无限期!

    这可能完全正常(也可能不正常),具体取决于您的分配模式。通常堆的工作方式是它们经常增长,但不经常收缩(这是为了有利于后续分配)。完全对称的分配和释放应该允许应用程序稳定在一定的使用量。

    例如:

    while(1) {
        free(malloc(100));
    }
    

    不应导致持续增长,因为堆很可能为每个 malloc 提供相同的块。

    所以我的问题是。它是“无限期地”增长还是根本不收缩?

    编辑:

    您已询问如何处理二维数组。就个人而言,我会使用一个类来包装细节。我要么使用一个库(我相信 boost 有一个 n 维数组类),要么滚动你自己的库应该不会太难。这样的事情可能就足够了:

    http://www.codef00.com/code/matrix.h

    用法如下:

    Matrix<int> m(2, 3);
    m[1][2] = 10;
    

    从技术上讲,使用 operator() 之类的东西来索引矩阵包装类在技术上更有效,但在这种情况下,我选择模拟原生数组语法。如果效率真的很重要,它可以像原生数组一样高效。

    编辑:另一个问题。你在什么平台上开发?如果它是 *nix,那么我会推荐 valgrind 来帮助查明您的内存泄漏。由于您提供的代码显然不是问题。

    我不知道,但我确信 windows 也有内存分析工具。

    编辑:如果您坚持使用普通的旧数组,为什么不将它分配为单个连续块并像这样对索引进行简单的数学运算:

    T *const p = new T[width * height];
    

    然后要访问一个元素,只需这样做:

    p[y * width + x] = whatever;
    

    这样,无论指针是一维数组还是二维数组,您都可以在指针上执行delete[]

    【讨论】:

    • 我还有一个分配二维数组的外部函数,所以我没有像你建议的那样简单地写它。我试图避免复制/粘贴所有行来动态分配二维数组。有什么聪明的方法吗?
    • 它无限增长,一段时间后,PC 挂起! (有点吓人!)
    • 您显然有内存泄漏,但它不在您发布的代码中。这应该可以工作,尽管它很脆弱(例如,不要将它与用户定义的类型一起使用)。尝试将所有指针转换为智能指针,并尽可能使用 STL 容器。
    • 我一直在 Linux 上进行开发,并且已经在使用 Valgrind。但是,我可能很愚蠢,无法理解输出。无论如何,我从 Valgrind 那里得到一个线索,上面的函数正在泄漏......
    • 这样的多数组编程在 C 语言中似乎很常见。我正在编写的代码是学术性的,我希望使两个(或更多)索引尽可能分开,以指出每个方向的含义。
    【解决方案2】:

    没有可见的内存泄漏,但是使用这样的代码存在内存泄漏的高风险。尝试始终将资源包装在一个对象 (RAII) 中。 std::vector 完全符合您的要求:

    void DiscontinuousGalerkin_Domain::computeFaceInviscidFluxes(){
            int e,f,n,p;
            std::vector<double> Left_Conserved(NumOfProperties);//create vector with "NumOfProperties" initial entries
    
            //do stuff with Left_Conserved
            //exactly same usage !
            for (int i = 0; i < NumOfProperties; i++){//loop should be "for (int i = 0; i < Left_Conserved.size();i++)" .size() == NumOfProperties ! (if you didn't add or remove any elements since construction
                 Left_Conserved[i] = e*f + n*p*i;//made up operation
            }
            Left_Conserved.push_back(1.0);//vector automatically grows..no need to manually realloc
            assert(Left_Conserved.size() == NumOfProperties + 1);//yay - vector knows it's size
            //you don't have to care about the memory, the Left_Conserved OBJECT will clean it up (in the destructor which is automatically called when scope is left)
            return;
    }
    

    编辑:添加了一些示例操作。您真的应该阅读有关 STL 容器的信息,它们是值得的!
    编辑 2:对于 2d,您可以使用:

    std::vector<std::vector<double> >
    

    就像有人在 cmets 中建议的那样。但是 2d 的使用有点棘手。您应该首先查看 1d-case 以了解发生了什么(扩大向量等)

    【讨论】:

    • 像 Properties[NumOfProperties][NumOfProperties] 这样的二维数组呢?是否也有性病?
    【解决方案3】:

    不,只要您在调用dvector 模板和free 之间没有做任何激烈的事情,就不会泄漏任何内存。什么告诉你有内存泄漏?

    请问,您为什么选择创建自己的数组而不是使用像vectorlist 这样的STL 容器?这肯定会为您省去很多麻烦。

    【讨论】:

    • 在代码执行命令顶部显示分配的内存无限增长!当我在 DiscontinuousGalerkin_Domain::computeFaceInviscidFluxes() 中注释所有内存操作时,内存停止泄漏。好吧,我根本不知道矢量。是否可以用它创建二维数组?
    • @Biga:vector 就像一个一维双精度数组,vector> 是一个二维数组等等......
    • 该命令的结果被寻址为 ARRAY[i][j]?如果“i”和“j”跨越不同的维度呢?
    • @Biga:由于向量是作为数组实现的,因此您可以使用索引符号。但是,这对其他容器无效。
    【解决方案4】:

    我没有在这段代码中看到内存泄漏。

    如果您在 C++ 上编写程序 - 使用 new/delete 代替 malloc/free。

    为避免可能的内存泄漏,请使用智能指针或 stl 容器。

    【讨论】:

    • 并确保对数组使用 delete []。这就是我喜欢 STL 容器的原因之一:不太容易搞砸。
    【解决方案5】:

    如果将n 的负值传递给dvector,会发生什么?

    也许您应该考虑更改函数签名以将无符号类型作为参数:

    template< typename T >
    T * dvector( std::size_t n );
    

    另外,作为一种风格,我建议您在提供内存分配功能时始终提供您自己的内存释放功能。就像现在一样,调用者依赖于知道dvector 是使用malloc 实现的(并且free 是适当的发布调用)。像这样的:

    template< typename T >
    void dvector_free( T * p ) { free( p ); }
    

    正如其他人所建议的那样,将其作为 RAII 类执行会更加健壮。最后,正如其他人所建议的那样,有很多现有的、经过时间考验的库可以做到这一点,因此您可能根本不需要自己动手。

    【讨论】:

      【解决方案6】:

      因此,这里讨论的一些重要概念帮助我解决了代码中的内存泄漏问题。有两个主要错误:

      • malloc 的用户定义类型分配有问题。但是,当我将其更改为 new 时,泄漏变得更加严重,这是因为我的一个用户定义类型有一个构造函数调用一个没有参数且没有正确内存管理的外部函数。由于我在构造函数之后调用了该函数,因此处理本身没有错误,而只是内存分配。所以 new 一个正确的构造函数解决了一个主要的内存泄漏问题。

      • 另一个泄漏与错误的内存释放命令有关,我能够使用 Valgrind 将其隔离(并且有点耐心以正确获取其输出)。所以,这是错误(请不要称我为白痴!):

        if (something){
                //do stuff
                return;    //and here it is!!!  =P
        }
        free();
        return;
        

      据我了解,这就是 RAII 可以避免这样的错误编程的地方。我实际上还没有将它更改为 std::vector 或 boost::scoped_array 编码,因为我仍然不清楚 a 是否可以将它们作为参数传递给其他函数。所以,我还是要小心delete[]

      无论如何,内存泄漏已经消失了(现在......)=D

      【讨论】:

        猜你喜欢
        • 2019-08-12
        • 1970-01-01
        • 1970-01-01
        • 2012-08-18
        • 1970-01-01
        • 2018-04-21
        • 1970-01-01
        • 2014-01-20
        • 1970-01-01
        相关资源
        最近更新 更多