C++ 可能存在的内存泄漏问题(个人经验总结)

1.几乎所有程序员都知道的,使用new申请的内存,已不再使用却没被delete;

破解之道:
方法很多,
最基础方法就是程序员自己注意 new 和 delete 配套出现;
比较高级一点的,就是模仿智能指针,使用引用计数器;
2.new出来的内存,被强转成其他类型,之后被释放,但内存并没有被清理干净;
比如:(示例伪代码,不追求可运行)
class Student{
private:
    int a;
    int b;
}

class Person{

}

void main()
{
    Student* pStu = new Student;
    Person* pPer = (Person*)pStu;
    delete pPer;
}
3.申请时使用 new[],但释放时使用 delete;

破解之道:
(new -- delete)
(new[] -- delete [])
4.比较不好排查的一种情况
示例,伪代码,不追求可运行性
class Base{
    int a;
}

class Person:public Base{
    int b;
}

void main()
{
    Base* p = new Person;

    delete p;
}

简单分析:
Person继承Base基类,所以Person内存中至少存在8字节(int a + int b),
而Base* p = new Person;是合法的,
delete p;也是合法的,
问题在于 delete p 时,只释放了Base中的资源,也就是(int a)资源,
导致派生类中(int b)资源意外丢失。


解决方法:
使用 虚析构函数

class Base{
public:
    virtual ~Base();
    int a;
}

class Person:public Base{
public:
    virtual ~Person();
    int b;
}
5.比较少见的情况
示例:伪代码,不追求可运行性

class Test{
void* p = new char[32];
}

void main()
{
    Test a,b;
    memcpy(&a,&b,sizeof(a));
}

简单分析:
memcpy(&a,&b,sizeof(a));
首先要知道 Test.p 也是4字节的数据块,只不过这里存放的是另一块内存的起始地址,
由于完全拷贝,导致指针 a.p = b.p ,
也就是说原先 a.p 中存放的地址被 b.p 地址覆盖,
使得原先 a.p 所指向的内存无法再被访问到;


破解之道:
当然是禁止对类实例进行memcpy,memset这类操作啦!
6.更稀少的情况:析构函数中抛出异常导致内存泄漏

class Test{
...
    ~Test()
    {
        ...todo    //假设因为某些条件,这里抛出异常了
        ...todo
    }
}

调用:
std::vector vTest;

简单分析:
vTest容器对象被销毁之前,会先销毁内部的Test对象实例,但Test析构函数在执行过程中,
因为某些原因,抛出异常,导致内存未被完全释放,甚至会引发程序后续一系列不明确行为;

破解之道:
不要在析构函数中抛出异常,
如果出现异常,可考虑使用std::abort强制中断程序,
更仁道的做法,是捕捉异常,但可能会有不良的运行后果,
具体如何抉择,视情况而定,
但是,但是,但是,最好的方法,当然是禁止在析构函数中抛出异常;

 

你可能感兴趣的:(C++)