最近在用C++写项目时遇到的一些动态分配上的易错点,所以写这篇博客来记录下。下面我会列举一些问题作为例子来总结一下动态分配的易错点,如有不对,欢迎指正。
我们经常会在一个函数中对指针进行动态分配。动态分配伴随着释放,不及时释放分配的内存会造成内存泄漏,那么我们应该什么时候在哪里释放已经分配的内存呢?
作用域结束后释放
常见的情况是在函数中动态分配了一个指针,我们在函数作用域结束时释放该指针,防止内存泄漏。下面是一个在作用域结束后释放的例子
void func()
{
int* p = new(int);
int a = 1, b = 2;
delete p;
p = NULL;
return;
}
我们在函数 func() 中申请了一个指针 p,并为它动态分配内存空间,我们在函数作用域结束前将分配给它的内存释放掉。
看起来很简单,好的,我们来看下一个例子:
void func_mutiBacks(int a , int b)
{
int *p = new(int);
if (b - a)
{
return;
}
delete p;
p = NULL;
return;
}
func_mutiBacks(1, 1); // 函数没有走到最后,提前返回,指针p没有释放,内存泄漏
函数 func_mutiBacks() 是一个多出口函数,即拥有多个返回点。 func_mutiBacks(1, 1) 函数会提前返回,此时指针p没有释放,内存泄露。
对于有多个出口的函数,我们在return之前释放指针内存,修改后的代码如下:
void func_mutiBacks(int a , int b)
{
int *p = new(int);
if (b - a == 0)
{// 在return前释放指针内存
delete p;
p = NULL;
return;
}
delete p;
p = NULL;
return;
}
func_mutiBacks(1, 1);
此时指针p正确被释放,不会造成内存泄漏。
利用析构函数释放指针内存
通过刚才的两个例子可以看到,一个函数在有多个出口的情况下,我们必须对每个出口都加一道“门”,必须在每个return前写一次释放。这样未免也太麻烦了!那有没有什么方法可以让我们不用思考这么多直接用呢?
我们知道析构函数会在类对象结束时自动调用并释放掉对象的内存。 所以我们在写项目的时候,可以将指针放在一个类中,在析构函数里释放申请的指针内存。 这样就不用考虑释放不及时造成内存泄露了。下面是一个利用析构函数释放指针内存的一个例子:
// 类CPoint
class CPoint
{
public:
CPoint();
~CPoint();
int* get(); // 指针 m_point*
private:
int* m_point;
};
CPoint::CPoint():m_point(NULL)
{
m_point = new int;
}
CPoint::~CPoint()
{
delete m_point;
m_point = NULL; // 销毁后要指向空
}
int* CPoint::get()
{// 返回指针
return m_point;
}
void func_mutiBacks(int a , int b)
{
CPoint p;
if (b - a == 0)
{
return;
}
int c = 1;
*(p.get()) = c;
std::cout << *(p.get()) << std::endl; // 输出1
return;
}
func_mutiBacks(1, 2);
此时指针 m_point会随着 对象p 生命周期的结束而自动释放内存,不会出现内存泄露的问题。
这只是一个很简单的例子,例子中我们也没用指针做其他事情,所以可能有人会觉得这样十分麻烦,但如果我们要频繁的动态分配一个指针,那还是建议封装到类里比较好。