代码之家  ›  专栏  ›  技术社区  ›  Dalin Seivewright

如果内存未被其他任何对象引用,则释放指针

c++
  •  2
  • Dalin Seivewright  · 技术社区  · 15 年前

    我有一个方法,它有几个指针作为参数。可以使用被调用方的命名指针调用此方法,也可以动态创建指向新对象的指针,并在调用方法时将其作为参数直接传入。

    myClass *myPtr = new myClass(...);
    myMethod(myPtr);
    

    维拉斯

    myMethod(new myClass(...));
    

    问题是,如果这两个选项都是有效的,如何正确地释放传入的指针?如果在程序中再次访问myptr,则在mymethod中删除myptr将导致崩溃。如果我不删除myptr,第二个选项将导致内存泄漏(如果使用)。使用这两个选项都有好处,因此它们都不应该破坏程序。

    除了使用STL之外,这个问题还有哪些解决方案?我需要实现我自己的垃圾收集器吗?

    11 回复  |  直到 15 年前
        1
  •  8
  •   Dmitry    15 年前

    我会说,在这种情况下 呼叫者 应该负责释放对象。您可以考虑各种选项,最简单的是:

    myClass myInstance = myClass;  // or myClass(arg1, arg2, ...)
    // and the pass it to your method like this:
    myMethod(&myInstance);
    

    您还可以考虑一些智能指针选项,例如 std::tr1::shared_ptr 或者是来自Boost的东西。

    更新: 如果你的方法能够 NULL -指针作为它的参数,完全没有问题:

    // this is your method declaration:
    void myMethod(const myClass *myPtr);
    
    // in your tests or wherever in your code you can call it like
    myClass myInstance = myClass;  // or myClass(arg1, arg2, ...)
    myMethod(&myInstance);
    // or like this:
    myMethod(NULL);
    // for as long as your method has something like this in it:
    if (myPtr)
        myPtr->someMethod();
    
        2
  •  3
  •   Arkaitz Jimenez    15 年前

    你可以用一个智能指针,比如 shared_ptr 从Boost。

    如果没有,你需要清楚地说明谁 拥有 对象。如果你要接管所有权或者把所有权留给来电者。
    如果你把它留给呼叫者,使用表单 function(new whatever()) 这不是个好主意,但泄露是来电者的责任。

    如果您打算接管所有权,请创建 下沉 方法,选择一个正确的名称是一个好主意,当然您需要在完成后自己删除对象。

        3
  •  2
  •   jldupont    15 年前

    首先,你需要分配 所有权 目标:你需要有一个一致的策略(和强制的!)关于“谁”有权删除对象。一旦这一点被明确地确定下来,你就不应该有遇到的问题。

    一些策略:

    1. 租赁对象:所有权由贷款人保留

    2. 对象被赋予:所有权被转移

    第二,对于 跟踪使用 对于对象,您需要一个基础结构,如“智能指针”。这里有两个类别需要关注:

    1. 对象是“单独引用”的,即只有一个“用户”

    2. 对象是“多引用”的,即一个时间点有多个“用户”

    对于(1),“跟踪信息”是指针本身,而在(2)中,您需要更多的基础结构。

        4
  •  2
  •   Loki Astari    15 年前

    这是一个关于所有权的问题。

    您需要决定谁拥有所有权(或者是否有共享所有权)。
    基本上,传球是个很糟糕的主意 不是 非常类似C++(这基本上是一个C接口,因为没有所有权的概念)。在C++程序中,您应该定义一个非常清楚的所有权转移的接口(函数)。

    你有几个选择。

    1. 函数没有所有权,不能为空。
      在这种情况下,您应该通过引用
    2. 函数没有所有权,但可能为空。
      作为指针传递并添加不传输所有权的注释。这是最糟糕的情况,您应该尝试避免这种情况,因为代码没有清楚地表达语义。
    3. 该功能拥有所有权。
      在这种情况下,我建议使用std::auto_ptr,因为它明确表示所有权正在转移到函数。
    4. 该功能共享所有权。
      在这种情况下,共享智能指针的某种形式,如boost::shared_ptr或std::tr1::shared_ptr

    在你的情况下,我建议1或2视情况而定。
    显然,函数不能删除指针,因为它有时可能没有所有权。因此,所有权仍然在呼叫者的一边。因此调用方必须根据需要调用delete。希望通过一些智能指针机制。

        5
  •  1
  •   shoosh    15 年前

    你应该使用某种智能指针。
    为了这个简单的案子 auto_ptr 很好,但一般来说你应该用 scoped_ptr shared_ptr .
    如果你对STL或Boost有一些无法解释的恐惧,你总是可以相对轻松地拉你自己的智能指针类(如果你不需要超级异常安全)。

        6
  •  0
  •   Douglas Leeder    15 年前

    您需要决定所有权,或者:

    1. 调用者拥有对象:对象被借给函数,调用者负责分配和删除。

    2. 函数拥有对象:对象被捐赠给函数,调用方负责分配,被调用方(函数)负责删除。

    3. 通过共享指针使用引用计数以避免该问题。

    (2是丑陋的,因为它意味着不同的代码负责分配和删除,但这可能是正确的解决方案-如果调用方想要保留对象,它还意味着复制参数)。

        7
  •  0
  •   jilles de wit    15 年前

    这两个都是C++语法意义上的有效选项,但我认为第二个选项在几乎所有的情况下都是糟糕的软件设计,并且允许一个函数中的两个选项在所有情况下都可能是糟糕的软件设计。

    要么 调用类创建对象、调用方法并删除对象,
    调用类创建对象,调用方法,并期望方法删除该对象。

    我认为在一个方法调用中,两个选项都应该被认为是合理的,这没有什么好的理由。从文档或注释中应该可以清楚地看到该方法使用的版本,然后由开发人员使用该方法,或者承担后果。

        8
  •  0
  •   MSalters    15 年前

    常见的行为是函数既不创建也不销毁对象。但是,有一些功能可以做到。常用的行话是“源流”。如果不使用更通用的智能指针,一个非常有用的约定是使用 std::auto_ptr<T> 对于水源和水槽。“source”函数返回创建为 auto_ptr<T> ,而sink接受类型为的参数 自动指针 .

    好处是,即使您忘记了源函数的返回值,也不会出现内存泄漏。在语句末尾,返回的auto_ptr将销毁返回的对象。同样的,很明显,这个论点传递给了 void sink(auto_ptr<T> unreferenced) { } 会在之前被摧毁 sink 返回。

        9
  •  0
  •   Stack Overflow is garbage    15 年前

    内存应该由其所有者释放。通常,所有者是分配内存的实体,但在某些情况下,您可能允许转移所有权。

    void foo() {
      myClass* p = new myClass(); // foo owns the pointer, so foo should release it again
    }
    
    void foo() {
      boost::shared_ptr<myClass> p = new myClass(); // foo allocates the memory, but *transfers* ownership to the smart pointer. The smart pointer is now responsible for freeing the object. (This is true for all types of smart pointers, including boost::scoped_ptr, std::auto_ptr or the C++0x std::unique_ptr.
    }
    
    void foo() {
      ScopedMyClass x; // ScopedMyClass is some class wrapper which *internally* calls `new myClass`, and so owns the allocation and is responsible for freeing it before the end of the wrapper class' lifetime.
    }
    

    具体来说,如果 myMethod 不分配内存,那么它也不应该释放内存。

    如何处理:

    myMethod(new myClass(...));
    

    别那么做 .如果您分配内存,您需要拥有它。首先存储指针,以便删除内存:

    myClass* p = new myClass(...);
    myMethod(p);
    delete p;
    

    或者更好,*首先不要动态地分配对象,消除所有权问题:

    myMethod(myClass(...));
    
        10
  •  0
  •   swarfrat    15 年前

    我还不能评论,但是……虽然我通常会强烈主张“造物主拥有它”的范式(你真的能证明其他的东西是正确的吗?)我认为

    myMethod(new myClass(...));
    

    使用方法几乎需要转移所有权。也许询问您试图用这种用法完成什么是明智的,并且可以用另一种方式处理它吗?像一个引用临时而不是指针的重载?

    myMethod(myClass& a) { return myMethod(&a); }
    
    myMethod(myClass(...))
    
        11
  •  0
  •   Matthieu M.    15 年前

    好吧,我想我会把我的小石头加到大厦里去…尽管我不确定它是否会被阅读。

    这是一个关于内存所有权的问题,它是你界面的角色传达这一含义:“这个方法是否会取得所有权”。

    为了更好地表达所有权,使用原始指针通常是不好的。STL有个混蛋 auto_ptr 哪个更适合这项任务(我正在等待即将到来的任务) unique_ptr 在那里很不耐烦)。

    对于一种方法,有几种方法可以接受参数:

    // Value
    void method(T);
    
    // Reference
    void method(T&);
    
    // Pointer
    void method(T*);
    
    // Smart Pointer
    void method(std::auto_ptr<T>);
    

    我略过了简历的资格认证,因为这与我无关。

    要点是,在这4个解决方案中,目标是明确的:

    • 值/引用/指针:调用方负责内存
    • 自动节拍 方法取得内存的所有权,这意味着调用方以后不能使用变量。

      std::auto_ptr myt=std::auto_ptr(new t()); 方法(MYT); assert(myt.get()==0);//myt不再保存任何内容!

    当然,这就是为什么 自动节拍 是一个野兽,因为它不遵守复制一个对象使被复制的对象保持明显不变的惯例:通常所有公共方法在前后都应该给出相同的结果,如果您公开它,引用计数在这里是一个角大小写。

    因此,如果调用者是否应该期望方法取得所有权,那么您的接口应该更清晰,一个简单的方法是使用重载。

    void method(T*); // do something
    
    void method(std::auto_ptr<T> p)
    {
      method(p.get());
    }
    

    很简单,你现在已经清楚谁在处理内存了!

    同样的道理,您也可以使用这个重载技巧来自动检查指针是否无效。

    void method(T&); // do something
    void method(T* p)
    {
      if (p) method(*p); else throw NullPointer("method");
    }
    

    但是我会避免滥用这个技巧,你最终会得到成千上万的方法。

    所以请记住: 所有权语义最好用代码表示 而不是在评论中。

    你马上就可以和它混合 使用RAII管理资源 这意味着你应该 永不分配 记忆到 原始指针 :使用智能指针表示资源的所有权,使用原始指针指向您不拥有的现有对象(可能为空,否则引用更好;)。