struct vs class

Jonathan M Davis jmdavisProg at gmx.com
Sun Nov 14 04:28:22 PST 2010


On Sunday 14 November 2010 04:14:29 spir wrote:
> On Sun, 14 Nov 2010 03:32:18 -0800
> 
> Jonathan M Davis <jmdavisProg at gmx.com> wrote:
> > On Sunday 14 November 2010 03:08:49 spir wrote:
> > > Hello,
> > > 
> > > 
> > > There seems to be 2 main differences between structs & classes:
> > > 1. structs instances are direct values, implement value semantics;
> > > while class instances are referenced (actually "pointed") 2. classes
> > > can be subtyped/subclassed in a simple way; structs cannot be really
> > > subtyped -- but there is the "alias this" hack
> > 
> > The main thing to remember between structs and classes is that classes
> > are polymorphic. If you want polymorphism, use a class. If you don't use
> > a struct. Now, if you want a type which is _always_ a reference, then
> > you should probably choose a class, but it's quite easy to have a struct
> > which is a reference type by having its member variables are on the heap
> > and don't get deep copied be a postblit constructor. It is more
> > immediately clear, though, that a class is a reference type.
> > 
> > You can get more nuanced on the reasons why you should pick a struct or a
> > class, but polymorphism is really what it generally comes down to. If
> > you never intend to subclass it or have it implement interfaces, then it
> > should probably be a struct.
> 
> Thank you, Jonathan.
> But what about the copy issue. For instance, in a parsing lib, I currently
> have a "Source" class that is a kind of "cursored" text (think at a text
> file), so it's only:
> 
> class Source {
>     string text;
>     uint index;
>     this (string text, uint index) {
>         this.text = text;
>         this.index = index;
>     }
> }
> 
> The advantages that match functions return a source that has stepped
> forward after successful match (hope you see what I mean, it's a common
> issue in parsing). Conceptually, it's pure data, meaning it should be a
> struct. Also, as you point above, it requires no polymorphism. But I made
> it a class, because: * It's created only once, so heavier creation on the
> heap is irrelevant. * It's passed from match method to match method huge
> numbers of time (can be millions of times when parsing code). If I had a
> struct, it would be copied! (Even if the text itself can be prevented from
> copy by putting it in a plain array, or "pointed", the fields are still
> copied.) * Higher-level patterns that delegate matching to sub-patterns
> directly get updated source (with new index), precisely because the source
> is referenced.
> 
> For instance, the actual match method of the Choice pattern type holds:
>         // ...
>         foreach (Pattern pattern ; this.patterns) {
>             try
>                 return pattern.check(source);	// *******
>             catch (MatchError e) {
>                 // ...
>             }
>         }
>         // ...
>     }
> 
> The marked line works because Source is referenced. Else, I would have to
> update source.index manually. Similarly, for a Composition pattern type
> (every sub-pattern must match in sequence):
> 
>         foreach (Pattern pattern ; patterns) {
>             result = pattern.check(source);
>             nodes ~= result.node;
>         }
> 
> The source is automagically updated by each sub-pattern match.
> 
> Thus, this a case where semantic (of value) and feature (no polymorphism)
> rationales seem both to conflict with practical programming reasons.

Well, like I said, it can be more nuanced. Polymorphism is the main thing, but 
other factors can matter. In this case, you have an efficiency problem in using a 
struct. The are several ways around that:

1. Make the struct a reference type, making its internals pointers or 
references.

2. Make the struct a value type, but make its internals pointers are references 
and use COW (copy-on-write).

3. Use a pointer to a struct.

4. Use a class.

Personally, I think that #1 can be useful, but generally that's probably not 
what you want. #2 doesn't work correctly right now due to bugs regarding 
destructors, and it's a pretty complicated solution anyway.

As for #3 and #4, if you're not going to use polymorphism, then there really 
isn't much difference between using pointers to structs and making the type a 
class - the main difference between a pointer and a reference being that a 
reference is polymorphic (if you had a pointer to a class, it wouldn't be 
polymorphic), so it's arguably better to use pointer to a struct than a class 
when it doesn't need to be polymorphic, but I suppose that it's a matter of 
taste. If you make it a struct, you at least _can_ stick it on the stack if you 
want though. It also makes them easier to copy, since you don't have to declare 
a clone() function or something similar. However, making it a class makes it 
obvious that it's intended to be used as a reference type rather than a value 
type, so it all depends on what you're trying to do.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list