删除void*指针引发的内存泄露

当一个void*指向一个class object时,我们对其执行delete操作,会引发未定义行为——可以确定的是该delete操作不会执行object的析构函数,会导致内存泄露。

考虑如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Foo{
public:
Foo(char* name){
m_name=name;
m_data=new char[100];
};
~Foo(){delete[] m_data;};
private:
char* m_data;
char* m_name;
};
// 定义了一个专门用来释放类对象的函数,使用void*是想要让不论是什么类型的对象都可以通过该函数释放
void GeneralDelete(void* ptr){
delete ptr;
}
int main(int argc,char* argv[])
{
Foo* foo=new Foo("foo");
// 意图调用GeneralDelete来执行Foo的析构函数
GeneralDelete(foo);
return 0;
}

这份代码编译是没有问题的。

但是,通过gprof分析代码的执行部分会发现,并没有调用Foo的析构函数:

01

上面的代码中,定义了一个GeneralDelete函数,用于统一释放动态分配的对象。该函数输入的参数声明为void*表示着不管传进什么类型的对象,都能统一释放。

这里,在调用GeneralDelete释放foo对象时,首先将它转换成void*类型,再delete。但是问题就出在这里,删除void*类型的foo导致了内存泄露,这是因为当使用delete操作符进行释放对象时,delete需要根据类型信息正确地释放指针所指向的内存块。操作符delete的工作原理大概可以用以下伪代码表示:

1
2
3
4
delete(obj *ptr){
call_destructors(ptr);
free(ptr);
}

即,首先调用对象的析构函数,然后释放该对象指针。在调用对象的析构函数前,首先需要知道对象的类型,如果不知道对象的类型,则无法知道该调用谁的析构函数。

由于对象foo传入函数被转换为void*,所以delete不会调用任何析构函数,所以,构造函数中动态分配的内存并没有被释放,导致内存泄露。

C++标准明确规定,针对void*指针做delete操作会引发未定义行为。所以尽量不要将一个普通对象转换为void*类型,也不要对void*对象做delete操作。

执行正确delete删除操作的代码:

1
2
3
4
5
6
int main(int argc,char* argv[])
{
Foo* foo=new Foo("foo");
delete foo;
return 0;
}
全文完,若有不足之处请评论指正。
本文标题:删除void*指针引发的内存泄露
文章作者:ZhaLiPeng
发布时间:2016年06月04日 10时51分
本文字数:本文一共有586字
原始链接:https://imzlp.me/posts/6978/
许可协议: CC BY-NC-SA 4.0
转载请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!