On Thursday, June 07, 2018 21:07:26 DigitalDesigns via Digitalmars-d-learn wrote: > class A; > > class B > { > A a = new A(); > } > > auto b1 = new B(); > auto b2 = new B(); > > assert(b1.a == b2.a)!! > > > I'm glad I finally found this out! This is not typical behavior > in most languages is it? > > I'd expect it to be translated to something like > > class B > { > A a; > this() > { > a = new A(); > } > } > > In C# it is different, can't remember if it is different in C++. > This has caused bugs in my code because the fields are all > pointing to the same data when I expected them to each have > unique data ;/ > > This method is error prone and the behavior should be reversed, > it should not break the majority of code. If one wants the > current behavior then static new could be used or something else.
Well, if that compiles now with a non-immutable class object, then that was a language improvement. In any case, yes, the class object would be shared across all instances of the class. _Every_ type in D has an init value that they get default-initialized to before the constructor is run (assuming that the type even has a constructor). In the case of both classes and structs, whatever the member variables are directly initialized with make up the init value. So, if you have something like struct S { int i = 42; } then every instance of S will start with the value of 42 for i, and if you have something like struct S { int i = 42; this(int j) { i = j; } } then S.i is 42 before the constructor is run and will then be whatever it gets assigned to in the constructor after the constructor has run. The situation with classes is exactly the same as structs except that you don't have direct access to the init value (since you only ever deal with class references, not the classes themselves). One key result of this is that the class object is fully initialized to its init value for its exact type before _any_ constructors are called, so you don't get that problem that C++ has where the object isn't fully its correct type until all of the constructors have been called (so calling virtual functions from a class constructor in D actually works, unlike C++). All of this is quite clean with value types, but in the case of member variables that are pointers, dynamic arrays, or reference types that would mean that if you had something like struct S { int* ptr = new int(42); } every instance of S would have the same exact value for S.ptr, which can be surprising and is why it's sometimes been suggested that it not be legal to directly initialize member variables with mutable, non-value types. However, historically, this really only mattered for dynamic arrays, because originally it wasn't legal to directly initialize any member variable that was a pointer or reference, because the compiler and runtime weren't sophisticated enough to handle it. Several years ago, it was made possible to directly initialize immutable class references, but that doesn't really cause any problems, since sharing an immutable object doesn't cause problems. However, if it's now possible for the init value of an object to contain a mutable class reference or pointer, then the problem does get worse, and arguably, it becomes more critical to just make it illegal in the case of member variables in order to avoid the surprises (and bugs) that come when folks misunderstand what it really means to directly initialize a member variable in D. In any case, the way that D works here is a direct result of how init values work, and it really doesn't make sense for it to work any other way. At most, it would make sense to simply make it illegal to directly initialize types where it would be a problem. - Jonathan M Davis