www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Array of class intances

reply YY <yyudhistira hotmail.com> writes:
Suppose I have a class :

class C {
  int a;
  int b;
  this(int a1, int b1) {
    a = a1;
    b = b1;
  }
}

And I want to make an array of class C instances :

C[] inst;
inst ~= new C(1,2);
inst ~= new C(3,4);

What happened if I make a copy of inst using dup? Are the values or the
pointers are copied?

C[] copyinst = inst.dup;

When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it
copies the contents.

But when I do this :

inst.a += 3;
assert(inst.a != copyinst.a);

It also fails.

What's the best method to copy (clone) array of instances?
Apr 08 2008
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
YY wrote:
 Suppose I have a class :
 
 class C {
   int a;
   int b;
   this(int a1, int b1) {
     a = a1;
     b = b1;
   }
 }
 
 And I want to make an array of class C instances :
 
 C[] inst;
 inst ~= new C(1,2);
 inst ~= new C(3,4);
 
 What happened if I make a copy of inst using dup? Are the values or the
pointers are copied?
 
 C[] copyinst = inst.dup;
 
 When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it
copies the contents.

This assert tells you that the data pointer of the arrays is not equal, which means each array has it's own copy of the class references. But, it doesn't mean the class instances themselves have been duplicated. This shouldn't fail: assert(inst[0] == copyinst[0]); meaning the first item in each array is the same reference, you can see the value of the reference by doing this: writefln("%x", cast(void*)inst[0]); writefln("%x", cast(void*)copyinst[0]);
 But when I do this :
 
 inst.a += 3;
 assert(inst.a != copyinst.a);
 
 It also fails.

That is because inst[0] and copyinst[0] both refer to the same class reference, therefore inst[0].a is the same variable as copyinst[0].a
 What's the best method to copy (clone) array of instances?

Add this method to your class C C dup() { return new C(a,b); } Then, instead of this: C[] copyinst = inst.dup; use: C[] copyinst; foreach(i; inst) copyinst ~= i.dup; Regan
Apr 08 2008
next sibling parent Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 YY wrote:
 Suppose I have a class :

 class C {
   int a;
   int b;
   this(int a1, int b1) {
     a = a1;
     b = b1;
   }
 }

 And I want to make an array of class C instances :

 C[] inst;
 inst ~= new C(1,2);
 inst ~= new C(3,4);

 What happened if I make a copy of inst using dup? Are the values or 
 the pointers are copied?

 C[] copyinst = inst.dup;

 When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means 
 it copies the contents.

This assert tells you that the data pointer of the arrays is not equal, which means each array has it's own copy of the class references. But, it doesn't mean the class instances themselves have been duplicated. This shouldn't fail: assert(inst[0] == copyinst[0]);

The above assert should of course read: assert(inst[0] is copyinst[0]); Regan
Apr 08 2008
prev sibling parent reply BCS <BCS pathlink.com> writes:
Regan Heath wrote:

 C[] copyinst;
 foreach(i; inst)
     copyinst ~= i.dup;
 
 Regan

this will give somewhat better performance because the ~= will allocate and copy a lot. C[] copyinst; copyinst.length = inst.length; foreach(i,c; inst) copyinst[i] = c.dup; if you want compact code and don't mind it being a bit confusing: C[] copyinst = inst.dup; foreach(inout i; inst) i = i.dup;
Apr 08 2008
parent reply bearophile <bearophileHUGS lycos.com> writes:
BCS:
 C[] copyinst = inst.dup;
 foreach(inout i; inst) i = i.dup;

I suggest you to use ref instead of inout, I presume inout keyword will be removed. lutger: T[] dup(T)(T[] a) { static if (is(typeof(T.dup))) { T[] result; result.length = a.length; foreach(i, val; a) result[i] = val.dup; return result; } else return a.dup; } Nice. Something like this (untested, it may need debugging!) may be useful for nested arrays (IsArray is true for dynamic or static arrays and false in every other situation): template DeconstArrType(T) { // similar to std.bind.DynamicArrayType static if (IsArray!(T)) alias typeof(T[0])[] DeconstArrType; else alias T DeconstArrType; } DeconstArrType!(TySub)[] deepDup(TySub)(TySub[] seq) { static if (is(typeof(TySub.dup))) { auto result = new DeconstArrType!(TySub)[seq.length]; foreach (i, sub; seq) result[i] = deepDup(sub); return result; } else { return seq.dup; } } A generic deepcopy() function that works with everything (that needs the support of a special method in Object class too) may be useful. Bye, bearophile
Apr 08 2008
next sibling parent BCS <BCS pathlink.com> writes:
bearophile wrote:
 
 Something like this may be useful for nested arrays

At one point I wrote a template that bundled up an array into a ubyte buffer in a way that could be shipped across a wire and rebuilt it on the other end. I didn't care how deep the arrays were nested.
Apr 08 2008
prev sibling parent reply YY <yyudhistira hotmail.com> writes:
 Something like this (untested, it may need debugging!) may be useful for
nested arrays (IsArray is true for dynamic or static arrays and false in every
other situation):
 
 template DeconstArrType(T) {
     // similar to std.bind.DynamicArrayType
     static if (IsArray!(T))
         alias typeof(T[0])[] DeconstArrType;
     else
         alias T DeconstArrType;
 }
 
 DeconstArrType!(TySub)[] deepDup(TySub)(TySub[] seq) {
     static if (is(typeof(TySub.dup))) {
         auto result = new DeconstArrType!(TySub)[seq.length];
         foreach (i, sub; seq)
             result[i] = deepDup(sub);
         return result;
     } else {
         return seq.dup;
     }
 }
 
 A generic deepcopy() function that works with everything (that needs the
support of a special method in Object class too) may be useful.
 

Great, thanks for the hints. I have another question. Do I have to delete each and every member of the array? Like this : foreach (ref i; inst) delete i; inst.length = 0; If I only do this : inst.length = 0; will all the instance items be automatically captured by garbage collector?
Apr 09 2008
next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 09 Apr 2008 20:52:05 +0200, YY <yyudhistira hotmail.com> wrote:

 Something like this (untested, it may need debugging!) may be useful =


 for nested arrays (IsArray is true for dynamic or static arrays and  =


 false in every other situation):

 template DeconstArrType(T) {
     // similar to std.bind.DynamicArrayType
     static if (IsArray!(T))
         alias typeof(T[0])[] DeconstArrType;
     else
         alias T DeconstArrType;
 }

 DeconstArrType!(TySub)[] deepDup(TySub)(TySub[] seq) {
     static if (is(typeof(TySub.dup))) {
         auto result =3D new DeconstArrType!(TySub)[seq.length];
         foreach (i, sub; seq)
             result[i] =3D deepDup(sub);
         return result;
     } else {
         return seq.dup;
     }
 }

 A generic deepcopy() function that works with everything (that needs =


 the support of a special method in Object class too) may be useful.

Great, thanks for the hints. I have another question. Do I have to =

 delete each and every member of the array? Like this :

 foreach (ref i; inst) delete i;
 inst.length =3D 0;

 If I only do this :

 inst.length =3D 0;

 will all the instance items be automatically captured by garbage  =

 collector?

I believe the easiest would be to do inst =3D null;
Apr 09 2008
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"YY" <yyudhistira hotmail.com> wrote in message 
news:ftj38l$19pr$1 digitalmars.com...
 Great, thanks for the hints. I have another question. Do I have to delete 
 each and every member of the array? Like this :

 foreach (ref i; inst) delete i;
 inst.length = 0;

 If I only do this :

 inst.length = 0;

 will all the instance items be automatically captured by garbage 
 collector?

If there are no other references to them, they will.
Apr 09 2008
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jarrett Billingsley wrote:
 "YY" <yyudhistira hotmail.com> wrote in message 
 If I only do this :

 inst.length = 0;

 will all the instance items be automatically captured by garbage 
 collector?

If there are no other references to them, they will.

While the G would be well within its rights to collect them if no other references exist, I'm pretty sure the current one doesn't. The problem is that setting the length to 0 will only modify the array reference, not the data that was in it. The memory block will still be referenced and the current GC doesn't know what parts of a memory block are actually in use. The array contents cannot generally be cleared if the length of a reference is set to 0 either, since there may be other references. So as long as a pointer or array references that block of memory (and yes, a 0-length array can still reference a block of memory, namely the one that will be used when '~=' is used on it) it won't be collected.
Apr 09 2008
prev sibling parent reply lutger <lutger.blijdestijn gmail.com> writes:
YY wrote:

 Suppose I have a class :
 
 class C {
   int a;
   int b;
   this(int a1, int b1) {
     a = a1;
     b = b1;
   }
 }
 
 And I want to make an array of class C instances :
 
 C[] inst;
 inst ~= new C(1,2);
 inst ~= new C(3,4);
 
 What happened if I make a copy of inst using dup? Are the values or the
 pointers are copied?

The values are copied. But the values in this case are Objects, which are always pointers under the hood. So in the end pointers are copied by value ;)
 C[] copyinst = inst.dup;
 
 When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it
 copies the contents.

'inst.ptr == copyinst.ptr' compares the adressess of the arrays themselves, independent of their content. It means the arrays are stored in different place in memory, but the content can still be the same. Try this: inst[0] == copyinst[0] // true and this: &inst[0] == &copyinst[0] // false
 But when I do this :
 
 inst.a += 3;
 assert(inst.a != copyinst.a);
 
 It also fails.
 
 What's the best method to copy (clone) array of instances?

There is no standard way, perhaps somebody has written something? If haven't found a need for this myself yet, I prefer to use structs is these cases. You could implement an .dup or .clone method in your classes and create such a functions yourself, something like this (caution, not tested): T[] dup(T)(T[] a) { static if (is(typeof(T.dup))) { T[] result; result.length = a.length; foreach(i, val; a) result[i] = val.dup; return result; } else return a.dup; }
Apr 08 2008
parent lutger <lutger.blijdestijn gmail.com> writes:
lutger wrote:
 
 If haven't found a need for this myself yet, I prefer to use structs is
 these cases. You could implement an .dup or .clone method in your classes
 and create such a functions yourself, something like this (caution, not
 tested):

Sorry for my careless spelling, I'm a bit tired.
Apr 08 2008