Last update November 20, 2011

Doc Comments /
Class



Classes    

Table of contents of this page
Classes   
Comments   
Q: What exactly can I do in a destructor? Can I call delete on members? Do I need to?   
Q: Why can't D's garbage collector just collect objects in "top-down" order so that it would be safe to reference sub-objects   
Q: If I have an Object, how do I find out exactly what the real type is?   
Q: Why does 'if(myobject==null)' crash?   
Q: How do I get the size of a class instance?   
Q: How do I copy class instances?   
Q: 'private' doesn't seem to do anything -- is it broken?   
Protection Attributes   
S: Threads   
Links   

Comments    

There seems to be some problem with the web-page for classes http://d-programming-language.org/class.html 1. The side-bar is missing 2. Following the title, the texts starts with "$({SPEC S}? Classes,"

Q: What exactly can I do in a destructor? Can I call delete on members? Do I need to?    

A: Never count on garbage collected reference members of a class being valid in a destructor. You cannot delete them because they may have already been deleted by the garbage collector by the time your destructor gets called. It generally shouldn't be necessary to delete things in your destructors. Destructors in D are really quite limited, and generally aren't needed at all. Garbage collection sweeps are triggered by new's and delete's so chances are, if your object is the only one holding references to its members, then they will get collected in short order anyway. Probably the only reasonable use cases for a destructor are 1) if you have members referencing non-gc memory that needs to be freed 2) if you're holding onto some (non-garbage collected) system resource like a system file handle or window handle. Note that if you have a reference to a D file object, like std.stream.BufferedFile you cannot touch it in your destructor, even though you might like to.

See the newsgroup thread for more discussion.

Q: Why can't D's garbage collector just collect objects in "top-down" order so that it would be safe to reference sub-objects    

A: In general this is impossible, because there may be a cycle of references. If A has a reference to B and B has a reference to A, somebody has to be collected first. Only one of them can have a nice "top-down" ordering. Same holds true for a cycle of any size. Not everyone can get the top-down ordering. So the simple solution is to just say that referring to your own garbage collected members in your destructor is not allowed. Unfortunately the compiler (as of DMD 1.014) is perfectly happy to let you make this error, and your code will probably work fine until one day when you make a change that introduces a cycle.

Q: If I have an Object, how do I find out exactly what the real type is?    

A: Use classinfo
 char[] WhatAmI(Object obj)
 {
     return obj.classinfo.name;
 }
You can also construct a new instance of a class that has a default constructor with:
 Object MakeNewOne(Object obj)
 {
     return obj.classinfo.create();
 }
or
 Object MakeNewOne(Object obj)
 {
     return Object.factory(obj.classinfo.name);
 }

A2: If the object's toString hasn't been overriden (which you can't count on), you could do this:
 char[] WhatAmI(Object obj)
 {
     return obj.toString; 
 }

Hopefully more reflection capabilities will be added to D sometime soon. I've read some comments from people that seem to think we'll get reflection when we get "AST Macros", but I don't know why that would be. -- JustinCalvarese

Q: Why does 'if(myobject==null)' crash?    

A: The short answer is that object comparisons like that should be done using 'is' rather than '=='. So
if(myobject is null) { . . . }

The longer answer is that myobject==something is turned into a call to myobject.opEquals(something), and clearly if myobject is null, this will result in a null pointer dereference.

Q: Could cases like this be added to some 'lint' type checks in the compiler. Obviously doing this can never be useful since the code can only crash or return false.

Q: How do I get the size of a class instance?    

A: obj.sizeof always returns 8, the size of the reference not the object itself. So how do you get the size of the instance itself?
  obj.classinfo.init.length
or
  Class.classinfo.init.length

Of course this won't work on something that isn't a class. It has been proposed a few times to add something like a .sizeofinstance or .isizeof property. For plain-old-data this would be identical to .sizeof, but for classes and objects it would evaluate to .classinfo.init.length.

Q: How do I copy class instances?    

A: Create a "dup()" method in your class. This is only a convention, but it's how the built-in types in D behave (namely, arrays) so it makes sense to extend this to user classes as well. In order to implement dup properly for hierarchies your base class should use `classinfo.create` to create an instance of the actual most derived type. Subclasses should then call super.dup() first, then copy their own members.

Note that for this to work properly, the class being duplicated must have a default constructor (i.e. "new TheClass?()" works). If it does not then classinfo.create will return a null object.

Example:

class Base {
    Base dup() {
       auto ret = cast(Base) this.classinfo.create;
       assert(ret !is null, this.classinfo.name ~ " has no default constructor!");
       ret.x = this.x;
       ret.y = this.y;
       return ret;
    }
    string toString() { return "I'm Base"; }
    int x = 6;
    double y = 8;
}

class DerivedA : Base {
    DerivedA dup() {
       auto ret = cast(DerivedA) super.dup;
       ret.w = this.w;
       return ret;
    }
    string toString() { return "I'm DerivedA"; }

    long w = 42;
} 

Q: 'private' doesn't seem to do anything -- is it broken?    

A: Actually 'private' in D means "module private". That means that any thing in the same source file as a class can access all of that class's members, private or otherwise. For the most part this is a way to avoid having to introduce a "friend" access feature like in C++ which can be used to give any class sneaky back door access to any other class.

Protection Attributes    

Where is the description of what protection attributes actually mean? It doesn't say what private,public,export, and package actually do. It also doesn't mention that 'protected' is accepted as an attribute too.

I think some of the information you're looking for is at: DigitalMars:d/attribute.html ("Protection Attribute" section). Even more information is available from: Qualifiers in D by Derek Parnell.

S: Threads    

What about a built in thread class?

  • A thread class is unique in that the thread must be started after ALL the contrustors are called and the thread must be stopped before ANY destructors are called. This implies a thread can't destroy its own object.
  • A thread will call the method slice() periodicaly (a period of zero is valid) or only when scheduled by a call to go().
   Standard methods

void slice() --- Code to be executed in a slice of thread time.

void go() --- Called once to start the allocating of slices and and called any time to schedule a new slice.

void waitFor() --- Blocks until the thread has stopped (the object may not have been destroyed).

void end() --- Does not block but causes the thread to stop when the current slice is complete. (Does not destroy the object).

this(int period, int priority, char[] name, int stack_size) --- Construct a thread calling slice every period micro seconds.

this(int priority, char[] name, int stack_size) --- Construct a thread with no periodic calls to slice. The slice is only called following a call to go.

Destroying the object implicitly stops the thread (end()) and waits for the thread to stop (waitFor()) before calling any destructors.

The constructor specifies the slice period (0 and infinite are both valid), the thread name as well as optionaly the stack size, thread priority and any other thread configuration properties (not all of which may be used as defined by the OS).

A thread object that starts execution on creation of the object must call go() in its constructor. If go() is not called in the objects constructor go() must be called subsequently to start execution. The destructor/end() can be called before go() is ever called in which case no slices are ever issued.

The method go() must be ISR safe as a slice could be sheduled from an IRQ. Also it must be constructor safe. The calls to go() are counted. If there are five calls to go() then there are five calls to slice() (in addition to the periodic calls to slice, if any).

--- DavidO?

Links    


FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: November 20, 2011 22:18 (diff))