[关闭]
@elibinary 2017-06-03T05:50:52.000000Z 字数 3060 阅读 780

[读书笔记] Ruby 的继承与方法查找

Ruby


在 ruby 中,在定义一个类时没有指定其超类,那么它的超类默认被指定为 Object

  1. class MyClass
  2. end
  3. MyClass.superclass
  4. # => Object
  5. class SonClass < MyClass
  6. end
  7. SonClass.superclass
  8. # => MyClass

可以看出,当 SonClass 继承 MyClass 时就是把 rb_classext_struct 中的 super 指针指向 MyClass

那么 Ruby 又是如何实现多继承的效果呢,说之前先说下 ruby 中 module 的概念。

module

在 Ruby 中,模块和类非常的相似,但他们是不同的,不同主要体现在三个方面:
1. 模块不能直接创建对象,也就是说模块没有 new 方法
2. 模块不能指定超类
3. 可以把模块包含进类中

除此之外,模块和类基本类似,其实 Ruby 内部实现模块的方式与实现类的方式相同,也是使用 RClass 和 rb_classext_struct 两个结构体来实现的。结构大致如下:

此处输入图片的描述

如图所示,在其结构体中还是存在 super 指针的,这是因为 module 在内部是有超类的,只是不允许手动去指定它。

那么在将 module include 进类中的时候发生了什么?

  1. module MyModule
  2. end
  3. class MyClass
  4. include MyModule
  5. end

当你 include 的一个 module 的时候,实际上 Ruby 会为该 module 创建一个结构体副本,然后把类的 super 指针指向该 module 副本,再把 module 副本的 super 指针指向该类原本的超类,其整个过程类似于链表结构的插入操作。

此处输入图片的描述

这样的超类链条就组成了 Ruby 类结构的超类链或者叫祖先链。

  1. MyClass.ancestors
  2. # => [MyClass, MyModule, Object, Kernel, BasicObject]

方法查找

Ruby 中的方法查找正是基于上面的超类链结构来进行的。而且整个算法核心思想比想象中的还要简洁,以上面为例

  1. my_obj = MyClass.new
  2. my_obj.one_method

此处输入图片的描述

  1. 首先找到当前对象的类(klass 指针指向的类),把该类作为当前类
  2. 在当前类的方法表(m_tbl)中查找方法
  3. 把当前类的超类(super 指针所指)作为当前类,再次执行2
  4. 重复上面 2、3 步直到找到方法

注意方法查找是沿着超类链顺序的向上查找的,一旦找到立刻返回。所以超类在其超类链中的顺序直接影响方法重载。

  1. module MyModule
  2. def say_hello
  3. puts 'Are you ok?'
  4. end
  5. end
  6. class MyClass
  7. def say_hello
  8. puts 'Hello, MyClass'
  9. end
  10. end

下面

  1. class SonClass < MyClass
  2. end
  3. SonClass.new.say_hello
  4. # Hello, MyClass
  5. SonClass

如果 include MyModule

  1. class SonClass < MyClass
  2. include MyModule
  3. end
  4. SonClass.new.say_hello
  5. # Are you ok?
  6. SonClass.ancestors
  7. # => [SonClass, MyModule, MyClass, Object, Kernel, BasicObject]

Ruby 内部是使用类继承来实现 module 的 include 的。本质上,include module 跟指定超类没有区别,它们都是通过使用类的 super 指针来实现,在类中 include 多个module 等价于指派多个超类。

但是,请注意一点,Ruby 内部依旧强制使用单一的祖先链,虽然你可以 include 多个 module 但是 Ruby 会让它们保持在一个单一的链表中,而这也同样使得方法查找变得简单统一。

  1. module MyModule
  2. def say_hello
  3. puts 'Are you ok?'
  4. end
  5. end
  6. module HerModule
  7. def say_hello
  8. puts "I'm a little fairy!"
  9. end
  10. end
  11. class MyClass
  12. def say_hello
  13. puts 'Hello, MyClass'
  14. end
  15. end
  16. class SonClass < MyClass
  17. include MyModule
  18. include HerModule
  19. end
  20. SonClass.new.say_hello
  21. # I'm a little fairy!
  22. SonClass.ancestors
  23. # => [SonClass, HerModule, MyModule, MyClass, Object, Kernel, BasicObject]

还有一点关于 superclass 方法
我本以为 #superclass 方法是直接返回的 super 指针所指

  1. module MyModule
  2. end
  3. class MyClass
  4. end
  5. class SonClass < MyClass
  6. include MyModule
  7. end
  8. SonClass.superclass
  9. # => MyClass
  10. SonClass.ancestors
  11. # => [SonClass, MyModule, MyClass, Object, Kernel, BasicObject]

可以看到,#superclass 跳过了 module,虽然它的 super 指针确实指向 module。
然后我就去找了 #superclass 的实现

  1. // ruby/object.c
  2. /*
  3. * call-seq:
  4. * class.superclass -> a_super_class or nil
  5. *
  6. * Returns the superclass of <i>class</i>, or <code>nil</code>.
  7. *
  8. * File.superclass #=> IO
  9. * IO.superclass #=> Object
  10. * Object.superclass #=> BasicObject
  11. * class Foo; end
  12. * class Bar < Foo; end
  13. * Bar.superclass #=> Foo
  14. *
  15. * Returns nil when the given class does not have a parent class:
  16. *
  17. * BasicObject.superclass #=> nil
  18. *
  19. */
  20. VALUE
  21. rb_class_superclass(VALUE klass)
  22. {
  23. VALUE super = RCLASS_SUPER(klass);
  24. if (!super) {
  25. if (klass == rb_cBasicObject) return Qnil;
  26. rb_raise(rb_eTypeError, "uninitialized class");
  27. }
  28. while (RB_TYPE_P(super, T_ICLASS)) {
  29. super = RCLASS_SUPER(super);
  30. }
  31. if (!super) {
  32. return Qnil;
  33. }
  34. return super;
  35. }
  36. VALUE
  37. rb_class_get_superclass(VALUE klass)
  38. {
  39. return RCLASS(klass)->super;
  40. }
  1. // ruby/include/ruby/ruby.h
  2. #define RCLASS_SUPER(c) rb_class_get_superclass(c)
  3. // ...
  4. #define RMODULE_SUPER(m) RCLASS_SUPER(m)

可以看出首先通过 rb_class_get_superclass 找到 super
然后在 while 循环中判断 super 的 type,最后返回 super

  1. // ruby/doc/extension.rdoc
  2. T_ICLASS :: included module
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注