April 09, 2010
Joseph Wakeling wrote:
> I also have some C++ experience, but it seems to be confusing as much as
> complementary with respect to D ... :-)
> 
> Current source of confusion relates to declaring objects of a class whose
> constructor takes input -- confusion because I can write,
> 
>         class Foo
>         {
>                 int x;
>                 uint y;
> 
>                 this()
>                 {
>                         x = -1;
>                         y = 2;
>                 }
>         }
> 
>         void main()
>         {
>                 Foo f;
>         }
> 
> and have no problem, but if instead the constructor is,
> 
>         this(int z)
>         {
>                 x = z;
>                 y = 2;
>         }
> 
> ... it seems like I have to write instead,
> 
>         auto f = new Foo(-1);
> 
> ... and if I write as per C++,
> 
>         Foo f(-1);
> 
> ... I get back a compiler error: "found 'f' when expecting ';' following
> 'statement'".  Am I right in thinking that the 'new' syntax is necessary when the
> class has a constructor which takes input?

You have stumbled upon a major difference between C++ and D.  In D, classes are reference types, and objects are allocated dynamically on the heap.  This means that if you simply type

  Foo f;

then f is null -- it is a reference to absolutely nothing -- regardless of whether Foo defines a constructor.  If you try to use it for anything, you will get a null dereference error.

To allocate a new object of type Foo, you use 'new':

  Foo f = new Foo;       // No constructor
  Foo f = new Foo(-1);

In both cases, f now points to a valid object of type Foo.  The C++ syntax you are referring to does not work in D.

If you want more C++-like behaviour, i.e. if you want to allocate on the stack, use a struct:

  struct Bar
  {
      uint x = -1;
      uint y = 2;

      this(uint z) { x = z; }
  }


  Bar b;            // Use default x and y values
  Bar b = Bar(0);   // Set x = 0

Note that you can also allocate a struct on the heap by using 'new'.  In this case, it returns a pointer:

  Bar* b = new Bar(123);


> This creates confusion also because in C++ one associates 'new' with dynamic
> allocation of memory, and it requires a consequent 'delete' statement.  I know
> that D has GC, and I know that it also has 'delete' statements, but ... this one
> is 'ouch' for me :-P

Now you see, 'new' is for dynamic memory allocation in D as well, it's just that for classes it is required.  You normally don't need to worry about 'delete', as the GC will take care of deallocation.

-Lars
April 09, 2010
Thanks for the interesting and detailed explanation. :-)

> Now you see, 'new' is for dynamic memory allocation in D as well, it's just that for classes it is required.  You normally don't need to worry about 'delete', as the GC will take care of deallocation.

I guess I am worried about what could happen in the case of code like this in C++:

    for(i=0;i<10000;++i) {
        Foo f(i);
        // Do something with f ...
    }

... when it reappears in D as:

    foreach(uint i;0..10000) {
        auto f = new Foo(i);
        // Do something with f ...
    }

Of course, it's not so terrible to have to put an explicit 'delete' statement at the end of the foreach loop if that is necessary (I am after all a C person at heart:-).  It's also clear that GC will probably make such a loop more efficient as it will manage the alloc/dealloc'ing of the memory more intelligently within the system constraints.  But the concern is there ...
April 09, 2010
I wrote:
> I guess I am worried about what could happen in the case of code like this in C++:
>
>     for(i=0;i<10000;++i) {
>         Foo f(i);
>         // Do something with f ...
>     }
>
> ... when it reappears in D as:
>
>     foreach(uint i;0..10000) {
>         auto f = new Foo(i);
>         // Do something with f ...
>     }

OK, I'm impressed. :-P

I did something equivalent to the above in my 'reputation' code, and ran it through Valgrind.  No difference in number of allocs/frees between a loop of 10000 and a loop of 1.

I think I'm no longer worried. :-)
April 09, 2010
Joseph Wakeling:
> ... when it reappears in D as:
> 
>     foreach(uint i;0..10000) {
>         auto f = new Foo(i);
>         // Do something with f ...
>     }

Also try:

foreach(uint i;0..10000) {
    scope Foo f = new Foo(i);
    // Do something with f ...
}

That sometimes doesn't require allocations.

Bye,
bearophile
April 09, 2010
> Also try:
>
> foreach(uint i;0..10000) {
>    scope Foo f = new Foo(i);
>    // Do something with f ...
> }
>
> That sometimes doesn't require allocations.

To be sure I understand -- is there anything wrong with writing

     scope auto f = new Foo(i)

... or, in general, using auto to infer the class being initiated?  I found myself disliking the double writing of Foo, although I guess there could be a positive side to it in ensuring that the class really is what it's meant to be.
April 09, 2010
Hello Joseph,

> To be sure I understand -- is there anything wrong with writing
> 
> scope auto f = new Foo(i)

BTW, the following might work:

scope f = new Foo(i);

-- 
... <IXOYE><



April 09, 2010
Joseph Wakeling:

> is there anything wrong with writing
>      scope auto f = new Foo(i)

If the compiler is cool with that, then it's OK :-)

Bye,
bearophile
April 09, 2010
Joseph Wakeling wrote:
>> Also try:
>>
>> foreach(uint i;0..10000) {
>>    scope Foo f = new Foo(i);
>>    // Do something with f ...
>> }
>>
>> That sometimes doesn't require allocations.
> 
> To be sure I understand -- is there anything wrong with writing
> 
>      scope auto f = new Foo(i)
> 
> ... or, in general, using auto to infer the class being initiated?  I found myself
> disliking the double writing of Foo, although I guess there could be a positive
> side to it in ensuring that the class really is what it's meant to be.

You don't have to write 'auto' to get type inference.  You just have to have something that signifies to the compiler that a variable is declared.  All of these are valid:

   auto x = 123;         // x is int
   const y = 1.23;       // y is const(double)
   static z = "hello";   // z is string
   scope f = new Foo(i); // f is Foo

It is a common belief that 'auto' means automatic type inference.  It doesn't.  'auto' is a storage class, the same as in C:

http://publib.boulder.ibm.com/infocenter/macxhelp/topic/com.ibm.vacpp6m.doc/language/ref/clrc03autdef.htm

-Lars
April 09, 2010
Lars T. Kyllingstad:
> You don't have to write 'auto' to get type inference.  You just have to have something that signifies to the compiler that a variable is declared.  All of these are valid:
> 
>     auto x = 123;         // x is int
>     const y = 1.23;       // y is const(double)
>     static z = "hello";   // z is string
>     scope f = new Foo(i); // f is Foo
> 
> It is a common belief that 'auto' means automatic type inference.  It doesn't.  'auto' is a storage class, the same as in C:

Thanks to you I am starting to understand, and I think it's a little strange :-) I think I prefer a keyword in the language to denote "use type inferencing here", orthogonal to the other attributes like scope or const (in C# the keyword to ask for type inference is "var": http://msdn.microsoft.com/en-us/library/bb383973.aspx ).
In the end the different is very small, but you, Don, and others have had to bang this in my head a dozen times before I have started to understand it :-(

Bye,
bearophile
1 2
Next ›   Last »