为何Valgrind检测到不存在的内存泄漏?附相关C++代码
为什么Valgrind误报了这段C++代码的内存泄漏?
这事儿我太清楚了!问题的根源全在你rebase函数的参数传递方式上——你用的是普通的指针值传递,而不是指针引用或者双重指针!
咱先一步一步捋代码的执行逻辑:
- 假设你在主函数里是这么调用的:
P* p = new P(); p->rebase(p); - 当调用
rebase(p)时,函数里的self参数是外面p指针的一个副本——也就是说,现在有两个不同的指针,都指向同一块P对象的内存。 - 你执行
delete self;:这确实释放了原来的P对象内存,没问题。 - 然后你执行
self = new C();:这只是给函数里的**局部副本指针self**赋值了新分配的C对象的地址,但外面的原指针p完全没变化,它还是指向那块已经被释放的内存地址(变成了悬空指针)。
这时候Valgrind的视角是啥样的?
- 它追踪到你分配了两块内存:一开始的
P对象,后来的C对象。 P对象被delete了,这块没问题。- 但
C对象的指针只存在于rebase函数的局部变量self里,函数执行完返回后,这个局部变量就被销毁了,Valgrind找不到任何指向C对象的指针会去释放它,所以它就判定这是内存泄漏。
你觉得“实际并无内存泄漏”,大概率是因为程序结束后操作系统会回收进程的所有内存,但Valgrind是严格追踪每一次new和delete的对应关系,它不管操作系统会不会兜底,只看你的代码有没有正确释放分配的内存。
怎么修复这个问题?
把rebase的参数改成指针引用,这样函数里修改的就是外面的原指针,而不是副本:
void P::rebase(P*& self) { // 这里改成指针引用 delete self; self = new C(); // 现在修改的是外面的原指针 }
或者用双重指针的写法:
void P::rebase(P** self) { delete *self; *self = new C(); } // 调用的时候要传地址:p->rebase(&p);
这样修改后,外面的指针会指向新分配的C对象,程序结束前你可以正常delete它,Valgrind也就不会再报泄漏了。
内容的提问来源于stack exchange,提问作者Jesse McDonald
相关产品推荐
相关产品推荐

