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/