WTF! new in class is static?!?!

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Jun 7 21:32:54 UTC 2018


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



More information about the Digitalmars-d-learn mailing list