【问题标题】:What is responsible for deleting my pointer?什么负责删除我的指针?
【发布时间】:2013-09-25 13:07:22
【问题描述】:

我经常想,

我知道我可以使用new 关键字在将相同指针作为参数传递给函数的同时创建指向对象实例的指针。就像我在下面的 Animation::newFrame 函数中一样,在下面的示例中给出。

但是,我也知道,作为一般规则,我有责任在使用 new 创建的内容上调用 delete

所以当我这样调用 Frame 的构造函数时:

Frame* myFrame
    = new Frame(new point(0,0), new point(100,100), new point(50,20));

最终释放我在上述函数调用中使用new 创建的 3 个点的内存的责任在哪里?

毕竟,以上 3 个新点并没有确切的名称供我称呼delete

我一直在某种程度上假设它们属于被调用的函数的范围,并且它们只会超出函数的范围。然而,最近我一直在想,也许不是这样。

我希望我在这里已经足够清楚了。

提前致谢,

男人

struct Frame
    {
    public:
        point f_loc;
        point f_dim;
        point f_anchor;

    //the main constructor:: Creates a frame with some specified values
        Frame(point* loc, point* dim, point* anchor)
        {
            f_loc = loc;
            f_dim = dim;
            f_anchor = anchor;
        }

    };

struct Animation
    {
    public:
        vector<Frame*> frameList;

    //currFrame will always be >0 so we subtract 1
        void Animation::newFrame(int &currFrame)
        {
            vector<Frame*>::iterator it;//create a new iterator
            it = frameList.begin()+((int)currFrame);//that new iterator is 

                    //add in a default frame after the current frame
            frameList.insert(
                        it,
                        new Frame(
                            new point(0,0),
                            new point(100,100),
                            new point(50,20)));

            currFrame++;//we are now working on the new Frame

        }

        //The default constructor for animation. 
        //Will create a new instance with a single empty frame
        Animation(int &currFrame)
        {
                frameList.push_back(new Frame(
                    new point(0,0),
                    new point(0,0),
                    new point(0,0)));

                currFrame = 1;
        }
    };

编辑:我忘了提到这个问题纯粹是理论上的。我知道原始指针有更好的选择,例如智能指针。我只是想加深我对常规指针以及如何管理它们的理解。

上面的例子也是取自我的一个项目,它实际上是 C++/cli 和 c++ 混合(托管和非托管类),所以这就是为什么构造函数只接受 point* 而不是按值传递(point )。因为point是一个非托管结构,因此在托管代码中使用时,必须由我自己,程序员来管理。 :)

【问题讨论】:

  • 该代码不可能编译。类型不匹配:f_loc = loc;也是-1。您应该尝试自己构建它。
  • 这就是为什么这些东西应该按值传递(而不是首先由new创建)。如果你用new创建它们,你必须在某个时候给它们打电话delete
  • 你为什么坚持在堆上分配对象(用new)?为什么不使用临时对象,例如:Frame myFrame(point(0,0), point(100,100), point(50,20));。它更便宜,因为它避免了您观察到的昂贵的堆分配和内存分配问题。
  • @Andrzej 我经常这样问自己。我很久以前就创建了这些结构,并且自从它们在我的代码中被广泛使用以来就没有改变它们。他们接受point* 而不是point,原因我已经记不得了。您上面的评论是完全正确的,但我将这个问题作为一个理论问题提出,只是为了更深入地了解指针及其所有权。
  • 您当前的代码也不是异常安全的。如果new 调用或point 构造函数之一抛出异常,您将泄漏成功的构造函数。

标签: c++ memory-management scope


【解决方案1】:

澄清并经常强制执行资源所有权的语义是程序员的责任。这可能是一件棘手的事情,尤其是当您在这里处理原始指针时,在资源所有权尚未考虑任何实际设计考虑的环境中。后者不仅在新手程序员编写的玩具程序中普遍存在,而且在具有数十年经验的人编写的生产系统中也很普遍。

在您上面的实际情况中,Frame 对象本身必须负责 deleteing 传入的 3 个指针,而构造 Frame 本身的任何东西都必须负责 deleteing 那个。

因为资源所有权就是这样一个雷区,程序员很早以前就发明了许多技术来澄清所有权的语义,让粗心的程序员更难引入错误和泄漏。如今,在 C++ 中,最好的做法是避免原始指针,事实上,尽可能完全避免动态分配——主要是因为资源所有权是一个危险的雷区。

在 C++ 中,用于实现这些目标的主要习惯用法是 RAII,主要使用的工具是 auto_ptr (C++03)、unique_ptrshared_ptr 及其类似工具。 Boost 还提供了许多所谓的“智能指针”。其中许多与 C++11 中的类似(实际上,C++11 中新的智能指针最初是由 Boost 开发的),但也有一些超越,例如 intrusive_ptr

【讨论】:

  • 我应该提到我熟悉并经验丰富的智能指针和 RAII 方法。你的意思是在我的Frame 构造函数中的某个时刻,我要调用delete loc;delete anchordelete dim? (如果我使用原始指针而不是智能指针。)
  • @GuyJoelMcLean 如果你是在路上newing 他们然后deleteing 他们在里面,那你为什么要使用new?只需按值传递points...
  • @GuyJoelMcLean John 说得很清楚:不要使用newdelete
【解决方案2】:

首先,您的代码需要修复以使其能够编译 - 例如,您可能无法将 point* 分配给 point(取决于 point 的实现)。

完成此操作后,您的问题的答案是Animation 需要释放其frameList 中的所有内容,Frame 需要释放传入的point*s。不会为您删除原始指针。

一个更好的答案(虽然不是您所要求的)是您应该使用shared_ptrunique_ptr 来管理内存 - 这些将为您删除指向的对象。这绝对是我要为frameList 做的事情 - 让它成为std::vector&lt;std::shared_ptr&lt;Frame&gt;&gt;

在这种情况下,可能没有充分的理由为points 使用指针(我猜它们只是坐标,复制起来非常便宜)——我只是通过值或 const 传递它们参考。这样就不会涉及堆分配,而且要简单得多。

【讨论】:

  • +1 用于通过值或常量引用传递点。它更简单,并且可能会执行得更好,因为它不需要一堆小的堆分配。
  • 谢谢,我知道这个例子不能单独编译。我实际的 point 结构实际上包含多个 operator= 重载,允许 point*point 赋值。
  • @GuyJoelMcLean 这太可怕了!
  • @SebastianRedl 为什么?正如正确猜测的那样,point 只是 2 个 GLfloats。 operator=() 函数只是将这些值从一个点复制到另一个点。或者从一点*到一点。它功能强大且可读性强。
  • T t; T u = &amp;t; 对其合法且与T u = t; 执行相同操作的任何类型 T 都是对隐式转换和/或运算符重载的可怕滥用,从长远来看只会造成混乱。如此弱化类型系统并没有什么可读的健壮性。例如,这允许类似于 Java 的 point p = new point(1, 2); 进行编译并且显然可以工作,除了它泄漏内存
【解决方案3】:

一个垃圾收集器,一个智能指针,有时它是自动的(如果你从堆栈中分配)。基本上,是你自己负责,但你可以将责任委托给其他地方。

【讨论】:

    【解决方案4】:

    如果您正在调用的函数没有明确声明它将在完成后获得传递的指针delete的所有权,那么您有责任对delete 自己负责new

    【讨论】:

    • 就像我上面的例子一样,我可以在函数参数上调用delete,只要它们是指针?
    【解决方案5】:

    从您的代码 sn-p 中可以看出,当前的内存策略是“传递所有权”。例如。您创建了 3 个点的实例,然后将它们传递给 Frame 构造函数——从此刻开始,Frame 实例负责管理这 3 个点的内存。因此,这意味着您必须为 Frame 类提供一个析构函数,该析构函数将负责删除作为成员存储的 3 个点中的每一个的内存。

    以上都是关于“原始记忆”技术的,这在新星时代是不必要的。请改用智能指针。例如,std::auto_ptr 是一个不错的候选对象(但请注意一些陷阱:如果将一个 auto_ptr 复制到另一个,源将被清除为 NULL,目标将拥有内存)。这里另一个广泛使用的候选者是 boost::shared_ptr(它甚至包含在最新的 C++ 标准中,作为 tr1:: 命名空间的一部分)。并且不要忘记:您必须了解针对您选择的智能指针的内存策略。否则,您最终将与被摧毁实例的幽灵交谈,否则您的实例将永远不会像瓶中的精灵一样被释放。

    【讨论】:

      【解决方案6】:

      这在很大程度上取决于约定。这看起来像 一个 GUI,而 GUI 中的通常约定是 包含对象一旦对delete负责 已通过指针。因此,Animation 将是 delete frameList 在其析构函数中的所有指针,以及 Frame 将删除它在其析构函数中保存的所有指针。 (请注意,此约定对于 GUI 非常特殊,但是, 并且在大多数其他情况下,其他约定将是 用过。)

      话虽如此,它在 C++ 中对于值也是非常不典型的 像 Point 开始动态分配,并且 也没有指向它们的指针。

      【讨论】:

        【解决方案7】:

        抱歉,没有完整阅读。 你有责任删除刚刚创建的对象,如果你没有对点的引用,你应该在你的框架对象中创建一个析构函数。 http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr380.htm

            class X {
            public:
              // Constructor for class X
              X();
              // Destructor for class X
              ~X();
            };
        

        X::~X() 会在 X 对象删除时自动调用。

        如果我错了,请纠正我。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-14
          • 1970-01-01
          • 2014-07-12
          • 2012-05-14
          • 1970-01-01
          • 2011-01-27
          • 1970-01-01
          • 2012-07-23
          相关资源
          最近更新 更多