• 内存泄露


    摘自《c/c++中常见内存泄露与对策及预防措施浅析》

    1 内存泄漏的发生方式
    以发生的方式来分类,内存泄漏可以分为以下四类。
    (1)常发性内存泄漏。

    发生内存泄漏的代码会被多次执行到,每次被执行时候都会导致一块内存泄漏。
    (2)偶发性内存泄漏。

    发生内存泄漏的代码只有在某些特定环境或操作过程中才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
    (3)一次性内存泄漏。

    发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致有且仅有一块内存发生泄漏。
    (4)隐式内存泄漏。

    程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格地说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存,但是对于一个服务器程序,需要运行几天几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存,所以称这类内存泄漏为隐式内存泄漏。

    2 常见内存泄露与对策

    (1)在使用局部指针变量或静态指针变量中的内存泄漏。

    例如:
    int main()
    {
    int *pi;
    pi=new int[100];
    r e tu rn;
    }
    这是常见的使用局部变量时出现的内存泄漏,没有释放指针变量。再如:
    int main()
    {
    static int *pi=0;
    for(int i=0;i<10;i++)
    pi=new int;
    r e tu rn;
    }
    这是常见的使用静态指针变量时出现的内存泄漏。程序退出后,分配的10个int只有最后一个是reachable的,而前面9个int则泄漏了。
    对策:释放全部指针变量。

    (2)在使用动态分配内存空间中的内存泄漏。

    例如:
    void MyFunction(int nSize)
    {
    char *p= new char[nSize];
    if(!GetString(p,nSize))
    {
    messageBox(“Error”);
    return;
    }
    ⋯ ⋯
    delete p;
    }
    这是一个简单而又典型的内存泄漏示例,当函数GetSt ring( )调用错误时,函数MyFunction结束而指针指向的内存却没有
    被释放,此时便出现了内存泄漏。在程序段入口处分配内存,在出口处释放内存,但是函数可以在任何地方退出,所以一旦有某个出口处没有释放应该释放的内存,就很容易发生内存泄漏。
    对策:C/C++函数可以在任何地方退出,在每个出口处释放应该释放的内存。本例中只要在return这个出口处释放内存p即可。

    (3)重复分配内存发生的内存泄漏。

    例如:
    void MyFunction(int nSize)
    {
    ⋯ ⋯
    char *p= new char[nSize];
    char *p= new char[nSize];
    ⋯ ⋯
    }
    对策:重复分配内存,第一块内存永远无法使用。这种情况一般多发生在编码过程中使用“代码复制”时出现错误,因此复
    制代码要谨慎。

    (4)非空指针被重新赋值发生的内存泄漏。
    给指针赋值时,没有检查指针是否为空,如果指针不空,那么指针原来指向的内存将泄漏。例如:
    char *p= new char[nSize];
    q = p ;
    对策:当q指针不为空,对其重新赋值后,q以前指向的内存泄漏。本例中可以再动态分配一个空指针,使p指向它。

    (5)缺少else处理分支导致的内存泄漏。
    例如:
    char *p= new char[nSize];
    iCount= SortProc(p);
    if(iCount<= 0)
    ret= 0;
    else if(iCount<= 5)
    ret= DealProc(p,iCount-1);
    if(ret<= 0)
    delete p;
    对策:当iCount>5时,ret取值不确定,当ret大于0时,没有释放p,造成内存泄漏。本例中需添加两个el se语句,用来判断
    iCount >5时ret的取值,以及ret大于0时,释
    放p。

    (6)删除指针顺序错误导致的内存泄漏。

    例如:
    Test ::~ Tes t()
    {
    delete(p);
    i f (NULL!= p && NULL!= p ->
    pData)
    {
    f ree(p->pData) ;
    }
    }
    对策:当p已经被删除了,那么if条件永远不成立,于是这条free语句永远不会被执行,即p->pData占用的内存没有被释放。本例中应调整删除指针顺序。

    (7)析构函数忘记释放内存导致的内存泄漏。

    例如:
    Test ::~ Tes t()
    {
    }
    对策:在析构函数里面对资源释放(非静态成员指针或资源)或清零(静态成员指针或资源)是一个良好的习惯,否则容易产
    生内存泄漏。

    (8)基类没有定义虚析构函数引起的内存泄漏。

    例如:
    classA
    {
    ~A(){}//析构函数不是虚函数
    }
    classB:publicA// classB继承了classA
    {
    ~B(){}
    }
    void main()
    {
    A *p = new B;
    delete p;

    }
    对策“: delete p”调用了classA的析构函数,没有调用B的析构函数,导致classB里面申请的资源泄漏。因为在C++标准中,通过基类的指针去删除子类的对象,而基类又没有定义虚析构函数时,结果将是不确定的。

  • 相关阅读:
    排序_简单选择排序
    排序_冒泡排序
    笔试_阿里_逆波兰表达式
    刷题_牛客_大整数排序
    刷题_牛客_字符串编码
    刷题_thinkinginjava_吸血鬼数字
    刷题_牛客_超级素数幂
    刷题_LeetCode_Two Sum
    刷题_LeetCode_Reverse Integer
    854. Floyd求最短路(模板)
  • 原文地址:https://www.cnblogs.com/caleb/p/2166190.html
Copyright © 2020-2023  润新知