www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What's the correct way of creating an instance of class in D?

reply Siarhei Siamashka <siarhei.siamashka gmail.com> writes:
C++ code:
```C++
#include <iostream>
class A {
public:
   void foo() { std::cout << "foo" << std::endl; }
};
int main() {
   auto a1 = new A;
   a1->foo(); // prints "foo"
   A a2;
   a2.foo();  // prints "foo"
   delete a1;
}
```
D code:
```D
 safe:
import std.stdio;
class A {
   void foo() { writeln("foo"); }
}
void main() {
   auto a1 = new A;
   a1.foo(); // prints "foo"
   A a2;
   a2.foo(); // Segmentation fault
}
```

I didn't expect to see a segmentation fault in the code, which is 
a straightforward conversion from C++. And especially not with 
the use of the ` safe` attribute. What's going on here?

```
$ ldc2 --version
LDC - the LLVM D compiler (1.30.0):
   based on DMD v2.100.1 and LLVM 14.0.6
   built with LDC - the LLVM D compiler (1.30.0)
   Default target: x86_64-pc-linux-gnu
```
Nov 02 2022
next sibling parent reply matheus <matheus gmail.com> writes:
On Thursday, 3 November 2022 at 04:41:14 UTC, Siarhei Siamashka 
wrote:
 ...
https://dlang.org/spec/class.html Matheus.
Nov 02 2022
parent reply Siarhei Siamashka <siarhei.siamashka gmail.com> writes:
On Thursday, 3 November 2022 at 05:10:06 UTC, matheus wrote:
 https://dlang.org/spec/class.html
Thanks for the link and also thanks for confirming that you have no clue what's going on. I think that what actually happens is that the D code ```D A a2; a2.foo(); ``` is roughly equivalent to C++ ```C++ A *a2 = NULL; a2->foo(); ``` I see two problems here. First, the D language documentation is incomplete and does not cover this particular syntax. And second, D language is designed (intentionally or accidentally) to be hostile to the C++ developers trying to learn it. This particular issue is known as https://en.wikipedia.org/wiki/False_friend
Nov 02 2022
parent reply Mike Parker <aldacron gmail.com> writes:
On Thursday, 3 November 2022 at 05:41:06 UTC, Siarhei Siamashka 
wrote:

 Thanks for the link and also thanks for confirming that you 
 have no clue what's going on. I think that what actually
That's not necessary. He does know what's going on and pointed you to the correct place. The second paragraph on the page says:
 Class objects are instantiated by reference only.
Then further down the page: https://dlang.org/spec/class.html#class-instantiation
 Instances of class objects are created with a NewExpression:
 happens is that the D code

 ```D
   A a2;
   a2.foo();
 ```

 is roughly equivalent to C++

 ```C++
   A *a2 = NULL;
   a2->foo();
 ```
That's correct. Classes in D are like Java classes. `a2` is a reference. Structs, on the other hand, are value types as they are in C++. D enforces the distinction that C++
 I see two problems here. First, the D language documentation is 
 incomplete and does not cover this particular syntax. And
I think that it does. But given that you missed it, then it could potentially be improved. Perhaps a new entry in the instantiation section that makes clear a declaration without an initialization is default initialized to null. The documentation is maintained by the community. You can post about issues you find here in the forums, or better, report them at issues.dlang.org (we're moving our bug tracking to GitHub soon).
 second, D language is designed (intentionally or accidentally) 
 to be hostile to the C++ developers trying to learn it. This 
 particular issue is known as 
 https://en.wikipedia.org/wiki/False_friend
There are similarities and differences. Any time you try out a language, you will view it through the lens of the language(s) you know, and you are going to encounter problems like this. I wouldn't call that hostility, intentional or accidental. But when you do encounter those differences in D, people here are willing to help you, so all you have to do is ask.
Nov 02 2022
parent Mike Parker <aldacron gmail.com> writes:
On Thursday, 3 November 2022 at 06:02:13 UTC, Mike Parker wrote:

 are in C++. D enforces the distinction that C++
...that C++ programmers often follow by convention.
Nov 02 2022
prev sibling next sibling parent Tejas <notrealemail gmail.com> writes:
On Thursday, 3 November 2022 at 04:41:14 UTC, Siarhei Siamashka 
wrote:
 C++ code:
 ```C++
 #include <iostream>
 class A {
 public:
   void foo() { std::cout << "foo" << std::endl; }
 };
 int main() {
   auto a1 = new A;
   a1->foo(); // prints "foo"
   A a2;
   a2.foo();  // prints "foo"
   delete a1;
 }
 ```
 D code:
 ```D
  safe:
 import std.stdio;
 class A {
   void foo() { writeln("foo"); }
 }
 void main() {
   auto a1 = new A;
   a1.foo(); // prints "foo"
   A a2;
   a2.foo(); // Segmentation fault
 }
 ```

 I didn't expect to see a segmentation fault in the code, which 
 is a straightforward conversion from C++. And especially not 
 with the use of the ` safe` attribute. What's going on here?

 ```
 $ ldc2 --version
 LDC - the LLVM D compiler (1.30.0):
   based on DMD v2.100.1 and LLVM 14.0.6
   built with LDC - the LLVM D compiler (1.30.0)
   Default target: x86_64-pc-linux-gnu
 ```
In D, all references and pointer types are default initialized to `null` no matter what, so you always have to explicitly assign an initial value if you dont want segfaults when dereferencing them ```d import std.stdio:writeln; void main(){ int* a; writeln(*a); // program killed by signal 11 int* b = new int(); writeln(*b); } ``` C++ initializes variable via the default constructor though, because it is a value type, like D's `struct`s ```cpp #include <iostream> using namespace std; class A{ public: A(){ this -> a = new int(44); } int* a; }; int main() { int* a; cout << *a << "\n"; //prints any garbage value A instance; cout << *instance.a << "\n"; // always prints 44 return 0; } ``` Use a pointer, and you'll get the same undesirable behaviour ```cpp #include <iostream> using namespace std; class A{ public: A(){ this -> a = new int(44); } int* a; }; int main() { int* a; cout << *a << "\n"; //prints any garbage value A* instance; cout << instance ->a << "\n"; // it's printing nothing for some reason return 0; } ``` You do get an error message if you use reference though, which D doesn't give even when using ` safe` C++: ```cpp #include <iostream> using namespace std; class A{ public: A(){ this -> a = new int(44); } int* a; }; int main() { int* a; cout << *a << "\n"; //prints any garbage value A& instance; cout << instance.a << "\n"; // it's printing nothing for some reason return 0; } main.cpp: In function ‘int main()’: main.cpp:28:8: error: ‘instance’ declared as reference but not initialized 28 | A& instance; | ^~~~~~~~ ``` D with ` safe`: ```d import std.stdio:writeln; void main() safe { int* a; writeln(*a); // still segfault int* b = new int(); writeln(*b); } ```
Nov 03 2022
prev sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, Nov 03, 2022 at 04:41:14AM +0000, Siarhei Siamashka via
Digitalmars-d-learn wrote:
[...]
 ```D
  safe:
 import std.stdio;
 class A {
   void foo() { writeln("foo"); }
 }
 void main() {
   auto a1 = new A;
   a1.foo(); // prints "foo"
   A a2;
   a2.foo(); // Segmentation fault
 }
 ```
[...] D does not have the equivalent of C++'s allocating a class instance on the stack. In D, all class instances are allocated on the heap and class variables are references to them. Declaring an instance of A as a local variable initializes it to the null reference, so invoking a method on it rightly segfaults. T -- Freedom: (n.) Man's self-given right to be enslaved by his own depravity.
Nov 03 2022
next sibling parent Adam D Ruppe <destructionator gmail.com> writes:
On Thursday, 3 November 2022 at 15:40:02 UTC, H. S. Teoh wrote:
 D does not have the equivalent of C++'s allocating a class 
 instance on the stack.  In D, all class instances are allocated 
 on the heap and class variables are references to them.
well there is scope Object o = new Object; which is stack allocated but still a reference of course
Nov 03 2022
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/3/22 08:40, H. S. Teoh wrote:

 D does not have the equivalent of C++'s allocating a class instance on
 the stack.
Not by default but we have two different ways, either may be discouraged: import std.typecons : scoped; import std.stdio; class C { int i; } void main() { // Either this: // auto c = scoped!C(); // Or this: scope c = new C(); writeln("If the address values are close, then the object is on the stack:"); writeln(&c); writeln(&c.i); } Ali
Nov 03 2022
prev sibling parent reply Tejas <notrealemail gmail.com> writes:
On Thursday, 3 November 2022 at 15:40:02 UTC, H. S. Teoh wrote:
 On Thu, Nov 03, 2022 at 04:41:14AM +0000, Siarhei Siamashka via 
 Digitalmars-d-learn wrote: [...]
 ```D
  safe:
 import std.stdio;
 class A {
   void foo() { writeln("foo"); }
 }
 void main() {
   auto a1 = new A;
   a1.foo(); // prints "foo"
   A a2;
   a2.foo(); // Segmentation fault
 }
 ```
[...] D does not have the equivalent of C++'s allocating a class instance on the stack. In D, all class instances are allocated on the heap and class variables are references to them. Declaring an instance of A as a local variable initializes it to the null reference, so invoking a method on it rightly segfaults. T
I think his main problem will go away if the code just refuses to compile, since it's known at compile time that one is trying to dereference a `null` pointer Check my post, `A& a;` refuses to compile in C++20 atleast, asking to be explicitly initialized, thus averting the problem altogether
Nov 03 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/3/22 1:46 PM, Tejas wrote:

 Check my post, `A& a;` refuses to compile in C++20 atleast, asking to be 
 explicitly initialized, thus averting the problem altogether
That's different, `A&` cannot be rebound in C++, whereas a class reference can. Try `A* a;` and see if it compiles -Steve
Nov 03 2022