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. -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Twitter: http://twitter.com/RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale