LoginSignup
5
8

More than 5 years have passed since last update.

Ruby: インスタンス変数とクラス変数

Posted at

インスタンス変数

インスタンス変数は「@」で始まる名前をしています。
インスタンス単位で持つ変数です。

sample-ivar.rb
class C
  def foo
    @count ||= 0
    @count += 1

    if @count < 3
      puts "foo は呼ばないでください."
    else
      puts "foo は呼ぶなッ!!#{@count} 度目!!"
    end
  end
end

c1 = C.new
c1.foo
c1.foo
c1.foo

puts '-' * 8

c2 = C.new
c2.foo
c2.foo
c2.foo

実行結果

$ ruby sample-ivar.rb 
foo は呼ばないでください.
foo は呼ばないでください.
foo は呼ぶなッ!!3 度目!!
--------
foo は呼ばないでください.
foo は呼ばないでください.
foo は呼ぶなッ!!3 度目!!

c1 も c2 もそれぞれ 3度目でキレます。

クラス変数

クラス変数は「@@」で始まる名前をしてます。
クラス単位で持つ変数です。

sample-cvar.rb
class C
  def foo
    @@count ||= 0
    @@count += 1

    if @@count < 3
      puts "foo は呼ばないでください."
    else
      puts "foo は呼ぶなッ!!#{@@count} 度目!!"
    end
  end
end

c1 = C.new
c1.foo
c1.foo
c1.foo

puts '-' * 8

c2 = C.new
c2.foo
c2.foo
c2.foo

実行結果

$ ruby sample-cvar.rb 
foo は呼ばないでください.
foo は呼ばないでください.
foo は呼ぶなッ!!3 度目!!
--------
foo は呼ぶなッ!!4 度目だ!!
foo は呼ぶなッ!!5 度目だ!!
foo は呼ぶなッ!!6 度目だ!!

c2 は最初からキレまくりです。(クラス通算3度目以降はキレます)

継承に関わるアレコレ

sample.rb
module M0
  @@v = :M0
  def foo ; "I am 'M0'" ; end
end

module MM
  @@v = :MM
  def foo ; "I am 'MM'" ; end
end

module M
  include MM
  @@v = :M
  def foo ; "I am 'M'" ; end
end

module PM
  @@v = :PM
  def foo ; "I am 'PM'" ; end
end

class A
  include M0
  @@v = :A
  def foo ; "I am 'A'" ; end
end

class B < A
  @@v = :B
  def foo ; "I am 'B'" ; end
end

class C < B
  @@v = :C
  def foo ; "I am 'C'" ; end
end

class D < C
  include M
  prepend PM
  @@v = :D

  def foo ; "I am 'D'" ; end

  def getv
    @@v
  end
end

d = D.new
p d.class.ancestors #=> [PM, D, M, MM, C, B, A, M0, Object, Kernel, BasicObject]
p d.getv
p d.foo

上のスクリプトを実行すると以下のようになります。

$ ruby sample.rb 
[PM, D, M, MM, C, B, A, M0, Object, Kernel, BasicObject]
:D                # メソッド getv の出力
"I am 'PM'"       # メソッド foo の出力

(もっともな話ですが) foo の出力は予想通りでした。
現在は PM の foo が呼ばれていますが、それをコメントアウトすると次は D の foo が呼ばれます。
それをコメントアウトすると次は M の.....としていくと継承順(下図の緑線)をたどって最後は M0 の foo が呼ばれます。
継承関係図を以下に示します。(青箱:クラス、橙二重線箱:モジュール、緑線:ancestors、青線:superclass)

sample.png

対して、「@@v」の値を出力する getv ですが、現在は D の@@vを参照しています。
これをコメントアウトすると......とやってみると以下の順番になります。

   D -> C -> B -> A -> M0 -> M -> MM -> PM   # クラス変数参照の順番

(これは図示しません。図を作ると美しくない図になります)

なんとなく、superclass をたどって折り返して来てインクルードされているモジュールを探索...、というようになっているようで、Ruby の仕様としては決まっているかも知れませんが、深く探求しません。
クラス変数に関しても、継承は複雑な問題をはらむようです。(で、強引にまとめます)

おわりに

本稿内容の動作確認は以下の環境で行っています。

  • Ruby 2.1.5 p273

付録

上の継承図のソース(Graphviz DOT形式)

sample.dot
digraph {
    rankdir=LR;
    node [shape = box];
    node [color = blue] A B C D;
    node [color = orange, peripheries = 2] M0 M MM PM;
    D    [style = filled, fillcolor = cyan];

    D -> C [color = blue];
    C -> B [color = blue];
    B -> A [color = blue];

    PM -> D  [color = green];
    D  -> M  [color = green];
    M  -> MM [color = green];
    MM -> C  [color = green];
    C  -> B  [color = green];
    B  -> A  [color = green];
    A  -> M0 [color = green];
}
5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8