Implementing .dup / clone for class hierarchies

Bill Baxter dnewsgroup at billbaxter.com
Mon Dec 10 12:09:58 PST 2007


Let me reply in reverse order:

Steven Schveighoffer wrote:
> One issue with your implementation, if I have a DerivedA reference to a 
> DerivedB class, and I call dup, I'm going to get a DerivedA class only, 
> wouldn't I want a complete copy (i.e. a DerivedB instance)?

No you'll get a complete copy because dup and copy are both virtual 
methods and so the most derived versions will be called.  Only the 
reference you get back will not be typed as the most derived type.  I 
believe that's all possible thanks to "covariant return types".

(Also in the example both DerivedA and DerivedB were derived directly 
from Base, so you wouldn't get a DerivedB from a DerivedA no matter how 
hard you try.  ;-) )

Here's a complete compiling example:

----------------------------------
class Base
{
     int x = 0;

     Base dup() {
        auto ret = new Base();
        // copy members of this to ret
        ret.copy(this);
        return ret;
     }
     void copy(Base other) {
         writefln("copy Base");
         x = other.x;
     }
     string toString() { return "Base"; }
}

class DerivedA : Base
{
     int y = 0;

     DerivedA dup() {
        auto ret = new DerivedA();
        ret.copy(this);
        return ret;
     }
     void copy(DerivedA other) {
         writefln("copy A");
         super.copy(other);
         y = other.y;
     }
     string toString() { return "DerivedA"; }
}

void main()
{
     auto a = new DerivedA;

     Base a_as_base = a;

     Base a_dup = a_as_base.dup;

     writefln("a_dup says I'm a: %s", a_dup);
     // prints
     //     copyA
     //     copy Base
     //     a_dup says I'm a: DerivedA
}



As for your suggestion:

 > If you have dup return an Object, then you can use the classinfo to 
allocate
 > a new object of the most derived type (I think), and then just 
override the
 > copy method.  The only issue is casting once you get the result, I 
like how
 > arrays don't need to be casted on dup.  But this could be overcome 
with a
 > wrapper dup that calls the base method and just casts the return. 
Ugly, but
 > doable.
 >

I tried it but to get it to work you have to make copy() take a Base 
rather than a Derived, otherwise DerivedA's copy() wont get called from 
Base.dup.  Thus copy methods will also all need casts with this method.

Here's the code:
-------------------------------------------
import std.stdio;

class Base
{
     int x = 0;

     Base dup() {
         auto ret = cast(Base)this.classinfo.create;
         // copy members of this to ret
         ret.copy(this);
         return ret;
     }
     void copy(Base other) {
         writefln("CopyBase");
         x = other.x;
     }
     string toString() { return "Base"; }
}

class DerivedA : Base
{
     int y = 0;

     DerivedA dup() {
         return cast(DerivedA)super.dup();
     }
     void copy(Base other) {
         writefln("CopyA");
         super.copy(other);
         auto d = cast(DerivedA)other;
         y = d.y;
     }
     string toString() { return "DerivedA"; }
}

void main()
{
     auto a = new DerivedA;
     Base a_as_base = a;
     Base a_dup = a_as_base.dup;
     writefln("a_dup says I'm a: %s", a_dup);
}

--bb


More information about the Digitalmars-d-learn mailing list