On 17.10.2009 16:19, Rick DeNatale wrote: > On Fri, Oct 16, 2009 at 12:30 PM, Caleb Clausen <vikkous / gmail.com> wrote: > >> Object#dup does not call new; I think it's more like: >> self.class.allocate.initialize_copy(self). See what happens here: >> >> irb(main):001:0> class K >> irb(main):002:1> def initialize >> irb(main):003:2> p :initialize >> irb(main):004:2> end >> irb(main):005:1> end >> => nil >> irb(main):006:0> k=K.new >> :initialize >> => #<K:0xb7ce8ee0> >> irb(main):008:0> k2=k.dup >> => #<K:0xb7ce0f38> > > And clone doesn't call initialize EITHER: > > class A > def initialize(iv) > @iv = iv > puts "initialize called" > end > > def initialize_copy(arg) > puts "initialize copy called, my iv is #{@iv}" > > end > end > > puts "Creating original" > a = A.new(42) > puts "calling dup" > a1 = a.dup > puts "calling clone" > a2 = a.clone > > outputs > > Creating original > initialize called > calling dup > initialize copy called, my iv is 42 > calling clone > initialize copy called, my iv is 42 > > It you look at the source code in object.c It becomes apparent that > Object#dup and Object#clone do pretty much the same thing except for > propagating the frozen bit and singleton classes: > > VALUE > rb_obj_clone(obj) > VALUE obj; > { > VALUE clone; > > if (rb_special_const_p(obj)) { > rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj)); > } > clone = rb_obj_alloc(rb_obj_class(obj)); > RBASIC(clone)->klass = rb_singleton_class_clone(obj); > RBASIC(clone)->flags = (RBASIC(obj)->flags | FL_TEST(clone, > FL_TAINT)) & ~(FL_FREEZE|FL_FINALIZE); > init_copy(clone, obj); > RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE; > > return clone; > } > > VALUE > rb_obj_dup(obj) > VALUE obj; > { > VALUE dup; > > if (rb_special_const_p(obj)) { > rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj)); > } > dup = rb_obj_alloc(rb_obj_class(obj)); > init_copy(dup, obj); > > return dup; > } > static void > init_copy(dest, obj) > VALUE dest, obj; > { > if (OBJ_FROZEN(dest)) { > rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", > rb_obj_classname(dest)); > } > RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR); > RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR|FL_TAINT); > if (FL_TEST(obj, FL_EXIVAR)) { > rb_copy_generic_ivar(dest, obj); > } > rb_gc_copy_finalizer(dest, obj); > switch (TYPE(obj)) { > case T_OBJECT: > case T_CLASS: > case T_MODULE: > if (ROBJECT(dest)->iv_tbl) { > st_free_table(ROBJECT(dest)->iv_tbl); > ROBJECT(dest)->iv_tbl = 0; > } > if (ROBJECT(obj)->iv_tbl) { > ROBJECT(dest)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl); > } > } > rb_funcall(dest, id_init_copy, 1, obj); > } > > > This code is from 1.8.6 just cuz that's what I happened to grab. > > In both cases the same subroutine is used to create the state of the > new object prior to calling intialize_copy and that subroutine > basically allocates the new object, copies instance variables "under > the table" and then invokes initialize_copy, no initialize method is > ever called on the result object. > > Which makes me thing that the whole "+dup+ typically uses the class > of the descendent object to create the new instance" is meaningless, > or untrue. Probably this is a vestige of an older implementation. That's what I'd guess, too. Basically the documentation should state for #dup and #clone something like this: "It is not normally necessary to override this method in subclasses. Customization of copying is done via method #initialize_copy." Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/