2

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

以下(非唯一)代码按预期运行。请注意 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's 时,事情就崩溃了:

...

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。关于如何自动和确定性地销毁容器拥有的对象有什么想法吗?

4

1 回答 1

3

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

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

坦率地说,从上到下在我看来都是错误的。我的建议:不要使用它。

现在,如果你写一个更正确的 Unique, with @disable this(this),在析构函数中检查一个空值,等等......你会发现它不起作用,std.container.DList因为在Dlist内部分配东西;它不使用移动操作。正确Unique的 withDlist不会编译。

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

现在我们可以看到引用计数发生了变化……但永远不会达到零。为什么?破解打开源代码DList,你会看到它在new Node内部使用。

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


最重要的是,您必须编写自己的 DList 容器。看起来std.container.Array实际上 /does/ 拥有所有权,它的析构函数确实破坏了有效负载(尽管它没有释放有效负载内存 - 所以它会调用析构函数。

因此,如果您使用更正确的引用计数实现,您可以使用Array- 我在上面发布的链接这样做是为了让您可以看到它有效,对 DList 的更改最小 - 但如果您确实想要一个链接列表,您必须自己动手在那里写一个适当的析构函数。

于 2014-07-21T14:13:20.490 回答