【问题标题】:Why is there no memory leak here为什么这里没有内存泄漏
【发布时间】:2014-07-17 09:39:11
【问题描述】:

我试图回答 this 的问题,所以我决定创建以下简单的测试用例,以便 OP 可以自己查看内存泄漏。

#include<iostream>

class MyObject
{
public:
   MyObject(){std::cout << "creation of my object" << std::endl;}
   virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
};

void processMyObject(MyObject foo)
{
}

int main()
{
   processMyObject(*new MyObject());
   return 0;
}

我编译了它:

g++ test.cpp -o test

然后,我看到了一个意想不到的输出:

creation of my object
destruction of my object

我完全不知道这里发生了什么。谁能给我解释一下?

PS:我用的是g++ 4.6.3

【问题讨论】:

  • 存在内存泄漏。您还需要检测复制并移动复制构造函数。

标签: c++ memory-leaks


【解决方案1】:

由于您将对象按值传递给函数,因此会产生复制或移动复制构造。但是您没有使用原始内存泄漏检查器来跟踪它。您可以提供自己的复制构造函数,然后您会看到正在创建两个对象,而只有一个正在被销毁:

#include<iostream>

class MyObject
{
public:
  MyObject() {std::cout << "creation of my object" << std::endl;}
  MyObject(const MyObject&) {std::cout << "copy creation of my object" << std::endl;}
  ~MyObject() {std::cout << "destruction of my object" << std::endl;}
};

void processMyObject(MyObject foo) {}

int main() 
{
  processMyObject(*new MyObject());
}

输出:

creation of my object
copy creation of my object
destruction of my object

【讨论】:

    【解决方案2】:

    因为您按价值获取MyObject

    于是就有了毁灭。但它是processMyObject末尾的foo参数的破坏。

    *new 在这种情况下实际上仍然会泄漏。

    编辑:正如 juanchopanza 所指出的,您还需要在复制构造函数和移动构造函数中打印一条语句。

    【讨论】:

    • @doctorlove 实际上,评论中已经指出了这一点。我编辑提到它:)
    • 哦,现在我明白了。我有点惭愧我自己找不到它。谢谢!
    【解决方案3】:

    你的代码会发生什么

    • 您构造一个对象并获取有关它的信息 (creation of my object)
    • 您将它传递给函数 - 复制构造函数触发,但不报告任何内容
    • 副本已销毁 - 您可以获取有关它的信息 (destruction of my object)
    • 尽管您没有任何关于它的信息,但原始实例仍会泄漏。

    怎么看?

    在构造和销毁过程中简单地报告指向this的指针(quick'n'dirty,请不要抱怨):

    class MyObject
    {
    public:
       MyObject(){std::cout << "creation of my object (" << (int)this << ")" << std::endl;}
       virtual ~MyObject(){std::cout << "destruction of my object (" << (int)this << ")" << std::endl;}
    };
    

    结果:

    creation of my object (165437448)
    destruction of my object (-1076708692)
    

    如您所见,销毁的对象与创建的对象不同。

    如何“修复”它以显示泄漏?

    “修复”代码的最简单方法是通过指针传递对象:

    #include<iostream>
    
    class MyObject
    {
    public:
       MyObject(){std::cout << "creation of my object" << std::endl;}
       virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
    };
    
    void processMyObject(MyObject * foo)
    {
    }
    
    int main()
    {
       processMyObject(new MyObject());
       return 0;
    }
    

    另一种选择是报告复制 ctor 和移动 ctor:

    class MyObject
    {
    public:
       MyObject(){std::cout << "creation of my object" << std::endl;}
       MyObject(const MyObject & obj) { std::cout << "copy-ctor" << std::endl; }
       MyObject(MyObject && obj) { std::cout << "move-ctor" << std::endl; }
       virtual ~MyObject(){std::cout << "destruction of my object" << std::endl;}
    };
    

    【讨论】:

    • @doctorlove 我也在那里发布了一个答案。打印指向它的指针以及构造/销毁报告是跟踪内存泄漏的一种简单而强大的方法:)
    【解决方案4】:

    实际上存在内存泄漏。仅仅因为您的对象被销毁,并不意味着您删除了您使用new 获得的资源。您必须明确使用delete

    编辑

    所以这就是发生的事情:

    1. 您正在动态调用默认构造函数,并将其传递给函数调用。这将打印第一条消息。
    2. 作为函数调用的一部分,对象将传递给processMyObject(),使用编译器隐式定义的复制构造函数在该范围内创建一个新对象。
    3. 当该对象(在processMyObject() 中)超出范围时,将调用其析构函数,并打印第二条消息。

    因此,打印第一条消息的实例与打印第二条消息的实例不同。

    希望能解决问题。

    【讨论】:

    • 是的,但问题是为什么我们可以看到某些内容被删除?
    猜你喜欢
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    • 2011-03-05
    • 2012-07-02
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    相关资源
    最近更新 更多