【问题标题】:How to confirm you are not having memory leaks?如何确认您没有内存泄漏?
【发布时间】:2018-03-25 12:19:30
【问题描述】:

所以..我正在尝试编写我的第一个析构函数。我有某事,但我不知道它是否真的在做它应该做的事情。

我有一个线性链表,其中每个节点都有 3 个动态分配的 char 数组和一个指向另一个也有 1 个动态 char 数组的链表的下一个指针 (heads)。换句话说,数据结构是链表的链表。这是我写的:

nodea * tempa = heada;//create a new node to
nodesongs *temps=heada->heads;

while(heada)// while the list is not empty
{

        tempa= heada ->nexta;// traverse the list
        delete [] heada->dataa.name ;
        delete [] heada->dataa.story ;
        delete [] heada->dataa.description ;

        while(heada->heads)
        {
                temps = heada ->heads->nexts;
                delete[] heada->heads->datas.title;
                delete heada->heads;
                heada->heads = temps;
        }
      delete heada;
      heada = tempa;

}

如果您能看一下,我将不胜感激,并且还想知道人们是否有一种常见的方法来测试他们的代码是否存在内存泄漏?

【问题讨论】:

  • 在现代 C++ 中,我们使用智能指针和 STL 容器来处理内存。这样做几乎可以消除任何内存泄漏的机会。您的代码非常适合研究低级的、过去的内存管理实践。这很重要,但此代码不应通过任何现代代码审查。
  • YAIT(又一个不称职的老师)
  • '奠定C++的基础' std::string 是基础
  • 如果你不能使用std::string,写一个简化的MyString。您不必手动处理列表中的字符串内存。
  • @Jarod42 这个。您将了解如何制作智能指针及其作用;这是有价值的。老师认为他们教你的东西(关于使用指针等)少了。

标签: c++ memory-management destructor


【解决方案1】:

如何确认您没有内存泄漏?

C++ 程序没有标准的方法来检查自己的状态并发现内存泄漏。

但是,有一些编程工具可以检测特定执行是否泄漏了内存。这些可以告诉你什么被泄露了,但这并不一定能帮助你理解为什么它被泄露了。

您可以做的是分析您编写的程序,并从逻辑上推断是否有任何手动分配的内存会泄漏。这通常很难做到(这就是编译器无法为您做到的原因),但如果您遵循最佳实践,它可以变得易于管理:

  • 永远不要手动管理内存(99.99% 的可能性您不需要)
    1. 永远不要打电话给new,也不要打电话给malloc
    2. 总是在收到拥有的指针后立即将它们包装在智能指针中
    3. 始终阅读您使用的库的文档。如果一个函数给你一个裸指针,请仔细检查你是否负责处理它;如果是,请参阅 2。
      • 这在处理具有 C API 的库时很常见。有时也会使用旧的或设计不佳的 C++ API。
  • 如果您确实手动管理内存(但不要这样做,因为您不需要)
    • 只在RAII时尚中这样做
      1. 拥有指针应该是类的私有成员。这种封装将您必须分析的范围从整个程序缩小到该类的成员函数。
        • 永远不要返回对该成员的引用或指针,只返回副本(甚至可能不返回)。返回引用会破坏封装(只有非常量引用,但通过 const 引用返回指针没有point)。
      2. 类不变量:指针必须始终指向 null 或有效的动态分配对象;该类的任何其他实例都不能指向该对象。
        • 如果允许成员不指向有效的动态分配对象(或 null),则处于这种状态的对象的析构函数将具有未定义的行为。
        • 如果允许多个对象拥有同一个指针,那么销毁一个对象会导致另一个对象违反指向有效对象的不变性。
        • 隐式复制/移动构造函数和赋值运算符执行浅拷贝,这将导致违反这些不变量。因此我们必须关注rule of 3 (or 5)
      3. 类的析构函数必须删除指针
      4. 该成员被分配的任何一次,要么删除旧值,要么将其所有权“转移”给另一个对象(除非指针具有未指定的值,由于 5. 它只能在构造函数中)。这些分配是您最有可能意外泄漏内存的地方。
      5. 每个类最多只有 1 个拥有的裸指针。如有必要,使用嵌套类。这个建议是为了让保证exception safety成为可能/更容易。缺乏异常安全性是更隐蔽的内存泄漏方式。

关于你的代码,让我们假设你必须手动管理内存,学习如何去做,所以我的建议 1 和 2 不是一个选项:

delete [] heada->dataa.name ;
delete [] heada->dataa.story ;

Bad 1:不要删除其他对象拥有的指针。这违反了我的建议 6。改为在该对象的析构函数中删除。

Bad 2:在容器对象 (*heada) 的生命周期内,不要让指针指向已删除的对象这是 Bad 1 的结果。违反了我的建议 5。

坏处 3:不要在单个对象中有多个裸露的拥有指针。这违反了我的建议 8。

【讨论】:

    【解决方案2】:

    评论扩展为答案:

    您应该努力编写内存泄漏不可能的代码,并且通过一些纪律可以合理地实现这一点。如果namespace std里的东西不让你用,那就自己写传真吧。

    例如唯一使用new的地方:

    template<typename T>
    struct pointer
    {
      template<typename ... TArgs>
      pointer(TArgs&&... targs) : p(new T(targs...)) {}
      pointer(const pointer & other) = delete;
      pointer& operator=(const pointer & other) = delete;
      pointer(pointer && other) : p(other.p) { other.p = nullptr; }
      pointer& operator=(pointer && other) { std::swap(this->p, other.p); }
      ~pointer { delete p; }
      T& operator*() { return *p; }
      T* operator->() { return *p; }
    private:
      T * p = nullptr;
    }
    

    【讨论】:

      【解决方案3】:

      没有明确的方法(参见例如Rice's theorem),但是:

      在某些情况下,使用一些(或考虑)garbage collector 可能会有所帮助。有时编写自己的标记 GC 代码既简单又值得。另见Boehm's GC。顺便说一句,有时 C++ 并不是最适合这项工作的语言。

      【讨论】:

        【解决方案4】:

        虽然 cmets 关于“使用智能指针”的说法是正确的,但它们并不能帮助您找到所需的内容。

        由于它依赖于平台,因此您正在寻找的工具是一种称为内存分析器的工具。

        有一些可用的,一些付费的,一些不可用的,并且大多数是特定于平台的。如果你在 Linux 上,我可以推荐的一个是 valgrind。

        【讨论】:

          猜你喜欢
          • 2013-04-30
          • 1970-01-01
          • 1970-01-01
          • 2012-12-11
          • 2013-11-24
          • 2017-01-13
          • 1970-01-01
          • 1970-01-01
          • 2016-08-29
          相关资源
          最近更新 更多