我们一般常说的内存泄漏是指堆内存的泄漏。程序从堆中分配的内存使用完毕后必须显式释放,否则这块内存就不能被再次使用,即这块内存泄漏了。内存泄漏导致软件在运行过程中占用越来越多的内存,程序的效率会越来越低,从而影响用户的体验,失去市场竞争力。
为了预防内存泄漏我们要求程序使用malloc、new等函数从堆中分配的内存必须在使用完后调用free、delete函数释放该内存。但是如果指向该内存指针的值被修改了,不再指向该内存了,那么即使调用free、delete函数也不会释放该内存,memset函数就会导致这种情况的发生。先来看一下memset函数的声明:
void *memset(void *s, int ch, size_t n); //将s中前n个字节用ch替换并返回s
假如指针s指向的类对象包含指针成员变量,那么在清零的过程中,就会将该指针的值置为0,不再指向原内存空间,原内存空间得不到释放导致内存泄漏。小编在近期的单元测试中就遇到了这个问题。问题发生在使用memset函数对包含string类型的结构体对象进行清零操作。我们先看一下的string类的构造和析构函数的简单实现:
class MString
{
public:
MString(const char *data = NULL);
……….
~MString();
private:
char *str_data;
};
MString::MString(const char *data)
{
try
{str_data = new char[strlen(data) + 1]; }
catch(const bad_alloc &e)
{ cout<<e.what(); abort(); }
strcpy(str_data, data);
}
MString::~MString()
{
delete[] str_data;
}
string类的构造函数在构造string对象时,str_data指针会指向堆中字符串长度加1大小的内存空间,而使用memset函数对string类型对象清零后str_data的值变成了0,指向的原内存空间在析构函数中不会被释放,导致内存泄漏。
因此我们可以得出结论:不要轻易零初始化string、vector等STL标准容器及具有动态内存管理的类。