当一个void*指向一个class object时,我们对其执行delete
操作,会引发未定义行为——可以确定的是该delete操作不会执行object的析构函数,会导致内存泄露。
考虑如下代码:
1 | class Foo{ |
这份代码编译是没有问题的。
但是,通过gprof
分析代码的执行部分会发现,并没有调用Foo的析构函数:
上面的代码中,定义了一个GeneralDelete
函数,用于统一释放动态分配的对象。该函数输入的参数声明为void*
表示着不管传进什么类型的对象,都能统一释放。
这里,在调用GeneralDelete
释放foo对象时,首先将它转换成void*
类型,再delete。但是问题就出在这里,删除void*
类型的foo导致了内存泄露,这是因为当使用delete操作符进行释放对象时,delete需要根据类型信息正确地释放指针所指向的内存块。操作符delete的工作原理大概可以用以下伪代码
表示:
1 | delete(obj *ptr){ |
即,首先调用对象的析构函数,然后释放该对象指针。在调用对象的析构函数前,首先需要知道对象的类型,如果不知道对象的类型,则无法知道该调用谁的析构函数。
由于对象foo传入函数被转换为void*
,所以delete不会调用任何析构函数,所以,构造函数中动态分配的内存并没有被释放,导致内存泄露。
C++标准明确规定,针对
void*
指针做delete操作会引发未定义行为。所以尽量不要将一个普通对象转换为void*
类型,也不要对void*
对象做delete操作。
执行正确delete删除操作的代码:
1 | int main(int argc,char* argv[]) |