【问题标题】:Why does valgrind see a memory leak here when there is none为什么没有时 valgrind 会在这里看到内存泄漏
【发布时间】:2018-03-21 19:32:22
【问题描述】:

我有以下一段 C++ 代码,暂时忽略在程序中实际执行此操作的不良做法。

#include<iostream>
//#include <type_traits>
using namespace std;

class P{
    public:
    void rebase(P* self);
    virtual void print();
    virtual ~P(){} 
};
class C: public P{
    virtual void print();

};
void P::rebase(P* self){
    //memory leak is detected, but no leak actually happens
    delete self;
    self=new C();
}
void P::print(){
    cout<<"P"<<endl;
}
void C::print(){
    cout<<"C"<<endl;
}


int main(){
    P *test;
    test= new P();
    test->print();
    for(int i=0;i<10000;i++) test->rebase(test);//run the "leaking" code 10000 times to amplify any leak
    test->print();
    delete test;
    while (true);//blocks program from stoping so we can look at it with pmap
}

我通过 valgrind 发送了这段很糟糕的代码,它在 P::rebase() 中报告了内存泄漏,但是当我查看内存使用情况时没有泄漏,为什么 valgrind 认为有?

==5547== LEAK SUMMARY:
==5547==    definitely lost: 80,000 bytes in 10,000 blocks
==5547==    indirectly lost: 0 bytes in 0 blocks
==5547==      possibly lost: 0 bytes in 0 blocks
==5547==    still reachable: 72,704 bytes in 1 blocks
==5547==         suppressed: 0 bytes in 0 blocks
==5547== Rerun with --leak-check=full to see details of leaked memory
==5547== 
==5547== For counts of detected and suppressed errors, rerun with: -v
==5547== ERROR SUMMARY: 30001 errors from 7 contexts (suppressed: 0 from 0)

我仔细检查了sudo pmap -x,没有泄漏

total kB           13272    2956     180

【问题讨论】:

  • 这是一个重要的问题,因为我设计的很多 C 程序都没有在调试器中声明内存问题,但 valgrind 会报告它们。

标签: c++ memory-leaks polymorphism valgrind


【解决方案1】:

您确实有内存泄漏。问题与

void P::rebase(P* self){
    //memory leak is detected, but no leak actually happens
    delete self;
    self=new C();
}

你是按值传递指针吗?这意味着来自 main 的指针永远不会重新分配新地址,并且您实际上会丢失它,因为 self 在函数结束时超出范围。如果你使用

void P::rebase(P*& self){
    //memory leak is detected, but no leak actually happens
    delete self;
    self=new C();
}

如果通过引用传递指针,则不会发生内存泄漏。


您的函数中也存在未定义,因为您不断从 main 对指针调用 delete 并多次对指针调用 delete,如果它不为 null,则为未定义行为。

基本上你的代码是一样的

int* a = new int;
for (int i = 0; i < 10000; i++)
{
    int* b = a; // we copy the address held by a
    delete b; // uh oh we call delete on that same address again
    b = new int; // put new memory in b, this does nothing to a
} // leak here as b goes out of scope and we no longer have the address it held

【讨论】:

  • 谢谢,我做了修改,valgrind现在没有检测到错误。
【解决方案2】:

Valgrind 是正确的。

void P::rebase(P* self){
    //memory leak is detected, but no leak actually happens
    delete self;
    self=new C();
}

这有两个问题——它没有返回指向程序的新指针 它会删除一个正在运行的对象。

删除正在运行的对象可能会导致函数末尾的代码崩溃,因为离开函数需要对象引用。

调用 test->print() 时代码可能会崩溃。我认为编译器正在为这两个对象重新使用内存。如果你把它们换了,例如

 P* old = self;
 self=new C();
 delete old;

那就不行了。

对 test 的非虚拟调用将起作用,但会导致未定义的行为。因为真正的对象在第一次调用后就被销毁了。

【讨论】:

    【解决方案3】:

    Valgrind 是一个软件,它有自己的逻辑,如果它的某些条件满足它认为是内存泄漏,它会根据它的逻辑创建警告列表。它不一定是完美的内存泄漏。如果您认为可以忽略此类警告,您可以直接忽略它。

    但在您的情况下,我可以看到明显的内存泄漏。您从类 C 实例化了一个对象,但您从未释放内存。这就是在 C++ 中鼓励我们使用智能指针来避免此类错误的原因。

    self=new C();
    

    【讨论】:

    • 在这种情况下,虽然它做得很好,因为 OP 实际上正在泄漏内存。
    • @NathanOliver 是的。我修改了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    • 2012-07-02
    相关资源
    最近更新 更多