【问题标题】:In D, is it possible for an object to hold a member object internally?在 D 中,一个对象是否可以在内部保存一个成员对象?
【发布时间】:2014-02-28 17:28:35
【问题描述】:

在 D 中,可以使用 scope 在堆栈上分配类,即

void foo()
{
    scope example = new Bar();
}

现在,如果Foo 类有Bar 类作为成员,有没有办法将Bar 就地存储在Foo 中,并用Foo 将其拆除? p>

我希望如此

import std.stdio;

class Foo {
    this() { writeln("Foo"); }

    ~this() { writeln("~Foo"); }

    scope Bar inside;
}

class Bar {

    this() { writeln("Bar"); }

    ~this() { writeln("~Bar"); }
};

void main()
{
    scope f = new Foo();
    writeln("I'm a main!");
}

会产生类似的东西

酒吧

我是主力!
~酒吧
~Foo

相反,我只得到

Foo
我是主力!
~Foo

似乎将类成员存储为垃圾收集引用而不是原地存储只是不必要地使堆布局复杂化而没有什么好处。如果没有指定将Bar 保留在原地,那么scope 在这种情况下会做什么?

【问题讨论】:

    标签: memory-management reference d object-lifetime


    【解决方案1】:

    不要使用范围类。这些实际上已被弃用,仅作为旧时代的遗产。有一个Phobos helper 可以达到类似的效果。您的示例重写:

    import std.stdio;
    import std.typecons;
    
    alias ScopedBar = typeof(scoped!Bar());
    
    class Bar {
    
        this() { writeln("Bar"); }
    
        ~this() { writeln("~Bar"); }
    };
    
    class Foo
    {   
        this()
        {
            this.inside = scoped!Bar();
            writeln("Foo");
        }
    
        ~this() { writeln("~Foo"); }
    
        ScopedBar inside;
    }
    
    void main()
    {
        writeln("Main before scope");
        {
            auto f = scoped!Foo();
        }
        writeln("Main after scope");
    }
    

    【讨论】:

      【解决方案2】:

      如果Bar 是一个结构,你会得到你想要的,而不需要范围(但是,结构不能有默认的构造函数,writeln("Bar") 是不允许的)。除非您需要接口和虚函数,否则我认为结构通常是在 D 中使用的更好工具,因为它们更灵活。你可以很容易地拥有一个具有确定性销毁、无间接等的作用域结构。如果你希望它成为一个引用,你总是可以使用指向它的指针或指向私有内部实现的指针来强制引用语义。

      可以将类包装在结构中,包括就地分配:

      import std.stdio;
      
      // bar is first because of https://d.puremagic.com/issues/show_bug.cgi?id=12278
      class Bar {
          this() { writeln("Bar"); }
          ~this() { writeln("~Bar"); }
      }
      
      
      class Foo {
          this() {
              writeln("Foo");
              // since there's no default ctor for structs, we force
              // initialization right here
              inside = InPlace!Bar.getDefault();
          }
      
          ~this() { writeln("~Foo"); }
      
          // InPlace instead of scope...
          InPlace!Bar inside;
      }
      
      struct InPlace(T) {
          // an in-place buffer for the class data...
          private byte[__traits(classInstanceSize, T)] rawData;
      
          @property T obj() { return cast(T) rawData.ptr; }
          alias obj this; // DANGER: don't escape this reference!
          @disable this(); // force initialization at the usage site
      
          // get it default-constructed
          static InPlace!T getDefault() {
              InPlace!T t = void;
              t.initializeObject();
              t.obj.__ctor();
              return t;
          }
      
          void initializeObject() {
              assert(__traits(classInstanceSize, T) == 8);
              assert(T.classinfo.init.length == 8);
              assert(this.rawData.length == 8);
              this.rawData[] = T.classinfo.init[];
          }
      
          // ctors with args
          this(T...)(T t) {
              initializeObject();
              obj._ctor(t);
          }
          ~this() {
              .destroy(obj); // call the class destructor in the struct dtor
          }
      }
      
      void main()
      {
          scope f = new Foo();
          writeln("I'm a main!");
      }
      

      编辑:std.typecons.scoped 也这样做,我不确定它是否会在班级成员中工作,但 Михаил Страшун 的回答表明确实如此。 /编辑

      这样可以解决问题...但我建议不要这样做。如果可以的话,你应该让 Bar 本身成为一个结构。这种包装魔法的危险在于,如果您逃避对内部对象的引用,如果首先破坏外部对象,您可能会随机崩溃。我猜如果它是私有的可能没问题,尽管那里仍然存在一些危险(D 中的私有是 module 私有的,所以该模块中的另一个函数可能仍然会逃避它,但是,如果你足够小心,你可以接受它。)

      跑步给出: 富 酒吧 我是主力! ~福 ~酒吧

      顺便说一句,您在 OP 中使用的范围事物目前已被弃用,因为它没有安全/完整地实施。我认为它现在甚至没有分配堆栈,它更像是在声明之后自动插入scope(exit) .destroy(foo);

      【讨论】:

      • 我想知道为什么 Scoped!T 结构本身是隐藏的(导致强制使用别名解决方法)
      • 我的猜测是不鼓励在函数本地范围之外使用它(或者只是过度使用 voldemort 类型;我认为它曾经是公开可用的——我似乎记得以前使用过 Scoped!T)
      • DPaste 在编译此代码时出错:dpaste.dzfl.pl/d659656fac86
      • 只需删除这些断言,它就会起作用,我将它们放入以追踪编译器错误。在 64 位上,类大小是 16 个字节,而不是我假设的 8 个字节。
      【解决方案3】:

      您没有得到Bar~Bar,因为您从未创建过该类的实例。

      【讨论】:

      • 这似乎是不言而喻的。我曾希望scope Bar inside; 会产生这种效果,尽管它似乎只是创建了对Bar 的引用,它是Foo 的成员。 static 在这种情况下做了什么?有没有办法做我想要完成的事情?
      猜你喜欢
      • 1970-01-01
      • 2020-05-30
      • 1970-01-01
      • 2020-09-04
      • 2016-11-22
      • 2018-12-16
      • 2013-05-24
      • 1970-01-01
      • 2011-09-25
      相关资源
      最近更新 更多