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



我试图实例化一个充满独特资源的容器,以确保当容器被销毁时,容器管理(拥有)的所有项目也被销毁,自动并立即销毁。

以下(非唯一)代码的行为与预期一致。注意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沉默。关于如何自动和确定地销毁我的容器拥有的对象,有什么想法吗?

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

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

坦率地说,我觉得从头到尾都不对。我的建议是:不要使用它。

现在,如果您使用@disable this(this)编写更正确的Unique,则在析构函数中检查null等....你会发现它不适用于std.container.DList,因为Dlist在内部分配东西;它不使用移动操作。正确的UniqueDlist不能编译

那么,让我们试试RefCounted。火卫一的RefCounted不能与类一起工作。太棒了。好吧,我们可以很容易地自己写,我只是在这里做了一个快速和草率的http://arsdnet.net/dcode/unique_refcounted.d U是更正确的Unique, RC是更正确的refcount。

现在我们可以看到refcount的变化…但永远不会达到零。为什么?打开DList的源代码,您将看到它在内部使用new Node

所以这些是由GC拥有的,因此在收集器运行之前不会被销毁。


底线是,您必须编写自己的DList容器。看起来std.container.Array实际上/确实/占有所有权,它的析构函数确实销毁了有效载荷(尽管它不释放有效载荷内存——所以它会调用析构函数。

所以如果你使用更正确的引用计数实现,你可以使用Array——我上面发布的链接就是这样做的,所以你可以看到它是有效的,对DList的修改很小——但是如果你想要一个链表,你必须自己动手,在那里写一个合适的析构函数。

相关内容

  • 没有找到相关文章

最新更新