代码之家  ›  专栏  ›  技术社区  ›  Vijay Angelo

C++习语以避免内存泄漏?

c++
  •  0
  • Vijay Angelo  · 技术社区  · 16 年前

    在以下代码中,如果 Info::addPart1() 意外多次呼叫:

    typedef struct
    {
    }part1;
    
    typedef struct
    {
    }part2;
    
    class Info
    {
        private:
        part1* _ptr1;
        part2* _ptr2;    
    
        public:
        Info()
        {
          _ptr1 = _ptr2 = NULL;
        }
    
        ~Info()
        {
          delete _ptr1; 
          delete _ptr2;
        }
    
        addPart1()
        {
           _ptr1 = new part1;         
        }
    
        addPart2()
        {
          _ptr2 = new part2;         
        }   
    };
    
    
    Info _wrapper;
    _wrapper.addPart1();
    _wrapper.addPart2();
    

    有C++习语来处理这个问题吗?

    我可以重写 addPart1 addPart2 像这样保卫MLK

    addPart1()
    {
      if(_ptr1 != NULL) delete _ptr1;
      _ptr1 = new part1;         
    }
    

    这是一个好的解决方案吗?

    11 回复  |  直到 12 年前
        1
  •  10
  •   SingleNegationElimination    16 年前

    使用智能指针,如 boost:shared_ptr , boost:scoped_ptr 建议管理原始指针。 auto_ptr 很难处理,你需要注意。

        2
  •  5
  •   the_drow    16 年前

    你应该读一下 smart pointer idiom 关于 RAII . 我建议看一下新的技术报告(TR1)。
    好好看看 here here .
    还可以看看Boost的智能指针。
    我推荐 loki-lib 的smartptr或strongtr类。

        3
  •  5
  •   timday    12 年前

    在这里忍受我…

    在遥远的过去,程序员使用诸如“跳转”和“goto”这样的结构来进行流控制。最终,出现了常见的模式和结构,比如for、do/while、函数调用和try/catch,意大利面也被驯服了。这些命名的构造提供了更多关于意图的信息,而不是一般的goto,在这里您必须检查代码的其余部分以了解上下文,以了解它在做什么。在不太可能的情况下,您会看到一个称职的编码人员在现代代码中发现了一个Goto,您知道发生了非常不寻常的事情。

    在我看来,“删除”是内存管理的“goto”。对于现代开发人员来说,有足够多的智能指针和容器类可供使用,大多数代码几乎没有理由包含单个显式删除(当然,智能指针实现除外)。当你看到一个简单的“删除”时,你不会得到关于意图的信息;当你看到一个作用域的容器时,你会得到更多。

    例如,习惯用法应该是希望使用适当的智能指针类型(这里几乎所有其他答案都建议这样做)编写删除自由代码。

    2013-01-27更新: 我注意到赫伯·萨特的 excellent talk on C++11 包括一些类似的情感重新删除自由代码。

        4
  •  4
  •   laalto    16 年前

    删除前检查非零指针是多余的。 delete 0 保证是不允许的。

    处理这种情况的常见方法是

    delete _ptr1;
    _ptr1 = 0;
    _ptr1 = new part1;
    

    将指针归零可确保不存在任何悬空指针,例如,在Part1构造引发异常的情况下。

        5
  •  3
  •   Geoff Romer    16 年前

    您建议的修复将起作用(当然,如果调用两次addPart2(),则仍然存在内存泄漏的风险)。一种更安全的方法是使用Boost库集合(www.boost.org)中的作用域指针,它是一个类似于指针的容器,但可以确保在容器被破坏时删除其目标。你的修改后的课程会看起来像

    class Info
    {
        private:
        boost::scoped_ptr<part1> _ptr1;
        boost::scoped_ptr<part2> _ptr2;    
    
        public:
        Info() {}  // scoped_ptrs default to null
    
        // You no longer need an explicit destructor- the implicit destructor
        // works because the scoped_ptr destructor handles deletion
    
        addPart1()
        {
          _ptr1.reset(new part1);
        }
    
        addPart2()
        {
          _ptr2.reset(new part2);         
        }   
    };
    

    作为一般原则,最好避免编写需要显式删除指针的代码。相反,尝试使用在适当的时间自动执行此操作的容器。对于这类事情,Boost是一种很好的资源。

    所有这些都假设你有一个原因,ptr1和ptr2必须是指针。如果不是,最好将它们变成普通对象;然后您可以免费获得内存管理。

        6
  •  2
  •   ralphtheninja    16 年前

    而使用构造是初始化。

    class Info
    {
        private:
        part1* _ptr1;
        part2* _ptr2;    
    
        public:
        Info() : _ptr1(new part1), _ptr2(new part2)
        {
        }
    
        ~Info()
        {
          delete _ptr1; 
          delete _ptr2;
        }
    };
    

    但在这种情况下,您也可以在堆栈上创建部件,因此不需要新建和删除。

    class Info
    {
        private:
        part1 _part1;
        part2 _part2;    
    
        public:
        Info()
        {
        }
    
        ~Info()
        {
        }
    };
    

    但是我想您希望指针被懒惰地创建,那么我不建议创建负责初始化的公共类方法。当类需要分配它们时,这应该在类内部处理。

        7
  •  1
  •   Joakim Elofsson    16 年前

    如果你希望它有懒惰的行为,你可以考虑这样做:

    addPart1()
    {
        if(_ptr1 == NULL) {
            _ptr1 = new part1;
        }
    }
    

    你建议的方式也是一种选择,这取决于你希望它如何表现。 但也有人提出了更好的方法,但是我们真的不知道你为什么这么做以及周围的代码是如何工作的…

        8
  •  1
  •   David Coufal    16 年前

    我同意小组的意见,你应该使用某种智能指针。

    如果您决定继续使用裸指针,请注意上面的类没有您定义的复制构造函数。因此,C++编译器已经为您定义了一个只做所有指针的简单副本;这将导致双重删除。您需要定义自己的复制构造函数(或者至少创建一个存根私有复制构造函数,如果您认为不需要复制构造函数的话)。

    Info(const Info &rhs)
    {
      _ptr1 = new part1[rhs._ptr1];
      _ptr2 = new part2[rhs._ptr2];
    }
    

    您将遇到与默认分配运算符类似的问题。

    如果选择正确的智能指针,这些问题将消失。:)

        9
  •  0
  •   Itay Maman    16 年前

    选项1:使用Java:

    选项2:使用自动指针

    std::auto_ptr<part1> _ptr1;
    std::auto_ptr<part2> _ptr2;
    
    public:
    addPart1()
    {
       _ptr1 = auto_ptr<part1>(new part1);
    }
    
    ...
    
    // no destructor is needed
    
        10
  •  0
  •   Silfverstrom    16 年前

    你应该看看 RAII

        11
  •  0
  •   SingleNegationElimination    16 年前

    处理内存泄漏的最极端的可能方法是 boehm 垃圾收集器,一个保守的Mark&Sweep收集器。有趣的是,除了其他答案中提供的所有好建议外,还可以使用这个方法。