确定性破坏容器拥有的对象(或如何将Unique(std.typecons.Unique)放入D Phobos容器中)?

时间:2014-07-21 04:50:00

标签: d unique-ptr raii deterministic phobos

我正在尝试实例化一个装满Unique资源的容器,试图确保当容器被销毁时,容器管理(拥有)的所有项目也会被自动且正确地销毁。

以下(非唯一)代码按预期运行。请注意,在应用程序退出之前,Foo对象不会被销毁(GC最终会回收它们)。暂时忽略GC,不要在DList被销毁时确定性地销毁它们 - 在“退出范围”消息 - 容器中的对象在应用程序生命周期内有效泄露:

import  std.stdio,
        std.container,
        std.range,
        std.typecons,
        std.random;

class Foo
{
  this()
  {
    debug( List ) writefln( "  %s constructor invoked", this.classinfo.name );
  }

  ~this()
  {
    debug( List ) writefln( "  %s destructor invoked", this.classinfo.name );
  }
}


int main( string[] args ) {
  debug( List ) writeln( "main():" );
  {
    debug( List ) writeln( "  entering scope" );
    scope auto list = DList!( Foo )();

    immutable ELEMENTS_TO_MAKE = 5;
    for( auto i = 0; i < ELEMENTS_TO_MAKE; ++i )
    {
      Foo foo = new Foo();
      list.insertBack( foo ); 
    }
    debug( List ) writefln( "  Length: %s elements in container", walkLength( list[] ) ); 
    debug( List ) writeln( "  exiting scope" );
  }
  debug( List ) writeln( "  exiting app" );
  return 0;
}

按预期提供以下输出:

main():
  entering scope
  main.Foo constructor invoked
  main.Foo constructor invoked
  main.Foo constructor invoked
  main.Foo constructor invoked
  main.Foo constructor invoked
  Length: 5 elements in container
  exiting scope
  exiting app
  main.Foo destructor invoked
  main.Foo destructor invoked
  main.Foo destructor invoked
  main.Foo destructor invoked
  main.Foo destructor invoked

但是当我更新应用程序以使用Unique时,事情就会崩溃:

...

int main( string[] args ) {
  debug( List ) writeln( "main():" );
  {
    debug( List ) writeln( "  entering scope" );
    scope auto list = DList!( Unique!Foo )();

    immutable ELEMENTS_TO_MAKE = 5;
    for( auto i = 0; i < ELEMENTS_TO_MAKE; ++i )
    {
      Unique!Foo foo = new Foo();
      list.insertBack( foo.release );  //looks like Phobos containers can't hold Unique's??? :( 
    }
    debug( List ) writefln( "  Length: %s elements in container", walkLength( list[] ) ); 
    debug( List ) writeln( "  exiting scope" );
  }
  debug( List ) writeln( "  exiting app" );
  return 0;
}

上面的代码给出了以下输出:

main():
  entering scope
  main.Foo constructor invoked
  main.Foo destructor invoked
Bus error: 10

注释掉list.insertBack()行会使总线错误10无效。有关如何自动和确定性地破坏容器拥有对象的任何想法?

1 个答案:

答案 0 :(得分:3)

这里最主要的问题是Phobos DList 取得对象的所有权 - 它在内部使用GC管理Node*,所以即使你修复了导致你看到的总线错误的空指针问题,你仍然不会得到你想要的破坏模式。

进入细节:Unique是错误的,它不应该是可复制的,但它是...它声称是多态的,但它只是最小的,并且它不检查在尝试删除其有效负载之前为null,这会导致您看到的总线错误。

坦率地说,从上到下我看起来不对。我的建议:不要使用它。

现在,如果您使用@disable this(this)编写更正确的唯一,在析构函数中进行空检查等等,您会发现它不适用于std.container.DList,因为{{ 1}}在内部分配内容;它不使用移动操作。带Dlist的正确Unique无法编译。

所以,让我们试试Dlist。 Phobos'RefCounted不适用于课程。太棒了。好吧,我们可以轻松地编写自己的内容,我只是在这里做了一个快速而草率的http://arsdnet.net/dcode/unique_refcounted.d U是更正确的唯一,RC是更正确的RefCounted。

现在我们可以看到refcount改变了......但是从来没有达到零。为什么?破解RefCounted的源代码,你会发现它在内部使用DList

所以这些都归GC所有,因此无论如何都要在收集器运行之前销毁。


总而言之,您必须编写自己的DList容器。看起来像new Node实际/做/取得所有权,它的析构函数确实破坏了有效负载(尽管它没有释放有效负载内存 - 因此它将调用析构函数。

因此,如果您使用更正确的引用计数实现,您可以使用std.container.Array - 我上面发布的链接会这样做,因此您可以看到它的工作原理,DList的最小更改 - 但是如果您确实需要链接列表,你必须DIY并在那里写一个适当的析构函数。