31

When should I define a type as a struct or as a class?

I know that struct are value types while classes are reference types. So I wonder, for example, should I define a stack as a struct or a class?

Cecil Ward
  • 597
  • 2
  • 13
Fred
  • 417
  • 6
  • 14

2 Answers2

39

Reason #1 to choose struct vs class: classes have inheritance, structs do not. If you need polymorphism, you must use classes.

Reason #2: structs are normally value types (though you can make them reference types if you work at it). Classes are always reference types. So, if you want a value type, choose a struct. If you want a reference type, it's easiest to go with a class.

Reason #3: If you have a type with a lot of data members, then you're probably going to want a reference type (to avoid expensive copying), in which case, you're probably going to choose a class.

Reason #4: If you want deterministic destruction of your type, then it's going to need to be a struct on the stack. Nothing on the GC heap has deterministic destruction, and the destructiors/finalizers of stuff on the GC heap may never be run. If they're collected by the GC, then their finalizers will be run, but otherwise, they won't. So, if you want your type to automatically be destroyed when it leaves scope, you need to use a struct and put it on the stack.

As for your particular case, containers should normally be reference types (copying all of their elements every time that you pass one around would be insanely expensive), and a Stack is a container, so you're going to want to use a class unless you want to go to the trouble of making it a ref-counted struct, which is decidedly more work. It just has the advantage of guaranteeing that its destructor will run when it's not used anymore.

On a side note, if you create a container which is a class, you're probably going to want to make it final so that its various functions can be inlined (and won't be virtual if that class doesn't derive from anything other than Object and they're not functions that Object has), which can be important for something like a container where performance can definitely matter.

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
  • Let's say may container class stores all the values in a node type: if I set it as a struct, whenever I remove a node and the node itself contains a reference value (object of a class), will the reference be deleted too? Exemple: List!Object lisObj; ... Object obj = new Object; lisObj.add(obj) lisObj.remove(Obj);//will obj still exist? – Fred Jun 10 '12 at 01:14
  • Just for curiosity, how complete is Phobo library regarding container? What it has and what is missing? – Fred Jun 10 '12 at 01:36
  • 1
    Anything allocated with new is garbage collected. If you want something on the heap to be freed at a specific time, then use malloc and free to manage their memory. As for Phobos' containers, they're in std.container, and they're currently rather sparse, because the custom allocator stuff is still being sorted out, and we're waiting for that to be done before writing all of the containers, because otherwise it would result in redoing a lot of work when we add custom allocators. If the built-in types and std.container aren't enough, take a look at http://www.dsource.org/projects/dcollections – Jonathan M Davis Jun 10 '12 at 03:54
6

Read "D"iving Into the D Programming Language

In D you get structs and then you get classes. They share many amenities but have different charters: structs are value types, whereas classes are meant for dynamic polymorphism and are accessed solely by reference. That way confusions, slicing-related bugs, and comments à la // No! Do NOT inherit! do not exist. When you design a type, you decide upfront whether it'll be a monomorphic value or a polymorphic reference. C++ famously allows defining ambiguous-gender types, but their use is rare, error-prone, and objectionable enough to warrant simply avoiding them by design.

For your Stack type, you are probably best off defining an interface first and then implementations thereof (using class) so that you don't tie-in a particular implementation of your Stack type to its interface.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • 1
    Defining an interface is not necessary, because there is duck typing (with compile-time checks). For example, D ranges can be used without interfaces, and usually are implemented as structs. – Roman Boiko Jun 14 '12 at 14:23
  • @RomanD.Boiko: Are you referring to templates? Also, `interface`s have certain advantages. – dirkgently Jun 14 '12 at 14:28
  • Yes, constrained template arguments. There are advantages for both approaches, I just stated that using interfaces is not necessary to avoid coupling. – Roman Boiko Jun 14 '12 at 14:32