struct vs class

spir denis.spir at gmail.com
Sun Nov 14 04:14:29 PST 2010


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.


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



More information about the Digitalmars-d-learn mailing list