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


179

Rubyインスタンス変数はいつ設定されますか?」と読みましたが、クラスインスタンス変数をいつ使用するは2つ考えています。

クラス変数はクラスのすべてのオブジェクトで共有され、インスタンス変数は1つのオブジェクトに属します。クラス変数がある場合、クラスインスタンス変数を使用する余地はあまりありません。

誰かがこれらの2つの違いとそれらをいつ使用するかを説明できますか?

次にコード例を示します。

class S
  @@k = 23
  @s = 15
  def self.s
    @s
  end
  def self.k
     @@k
  end

end
p S.s #15
p S.k #23

私は理解しました、クラスインスタンス変数は継承チェーンに沿って渡されません!

回答:


276

クラスのインスタンス変数:

class Parent
  @things = []
  def self.things
    @things
  end
  def things
    self.class.things
  end
end

class Child < Parent
  @things = []
end

Parent.things << :car
Child.things  << :doll
mom = Parent.new
dad = Parent.new

p Parent.things #=> [:car]
p Child.things  #=> [:doll]
p mom.things    #=> [:car]
p dad.things    #=> [:car]

クラス変数:

class Parent
  @@things = []
  def self.things
    @@things
  end
  def things
    @@things
  end
end

class Child < Parent
end

Parent.things << :car
Child.things  << :doll

p Parent.things #=> [:car,:doll]
p Child.things  #=> [:car,:doll]

mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new

[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]

(そのクラスのインスタンスではなく)クラスのインスタンス変数を使用すると、サブクラスが自動的にそれらを取得することなく(およびその逆も)、そのクラスに共通するものを格納できます。クラス変数を使用すると、self.classインスタンスオブジェクトから書き込む必要がないという利便性があり、(必要な場合)クラス階層全体で自動的に共有されます。


これらを1つの例にマージして、インスタンスのインスタンス変数もカバーするようにします。

class Parent
  @@family_things = []    # Shared between class and subclasses
  @shared_things  = []    # Specific to this class

  def self.family_things
    @@family_things
  end
  def self.shared_things
    @shared_things
  end

  attr_accessor :my_things
  def initialize
    @my_things = []       # Just for me
  end
  def family_things
    self.class.family_things
  end
  def shared_things
    self.class.shared_things
  end
end

class Child < Parent
  @shared_things = []
end

そして実際に:

mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new

Parent.family_things << :house
papa.family_things   << :vacuum
mama.shared_things   << :car
papa.shared_things   << :blender
papa.my_things       << :quadcopter
joey.my_things       << :bike
suzy.my_things       << :doll
joey.shared_things   << :puzzle
suzy.shared_things   << :blocks

p Parent.family_things #=> [:house, :vacuum]
p Child.family_things  #=> [:house, :vacuum]
p papa.family_things   #=> [:house, :vacuum]
p mama.family_things   #=> [:house, :vacuum]
p joey.family_things   #=> [:house, :vacuum]
p suzy.family_things   #=> [:house, :vacuum]

p Parent.shared_things #=> [:car, :blender]
p papa.shared_things   #=> [:car, :blender]
p mama.shared_things   #=> [:car, :blender]
p Child.shared_things  #=> [:puzzle, :blocks]  
p joey.shared_things   #=> [:puzzle, :blocks]
p suzy.shared_things   #=> [:puzzle, :blocks]

p papa.my_things       #=> [:quadcopter]
p mama.my_things       #=> []
p joey.my_things       #=> [:bike]
p suzy.my_things       #=> [:doll] 

@Phronzコードで言及したself.thingsとself.class.thingsの違いは何ですか?
サイボーグ

1
@cyborg は現在のスコープ内のself.thingsメソッドthingsを参照しました(クラスのインスタンスの場合は、インスタンスのメソッドになります)。現在のスコープのクラスからメソッドをself.class.things参照しthingsます(クラスのインスタンスの場合も同様)クラスメソッド)。
グラフィゾン

美しい説明。
aliahme922

30

主な(唯一の?)違いは継承だと思います:

class T < S
end

p T.k
=> 23

S.k = 24
p T.k
=> 24

p T.s
=> nil

クラス変数はすべての「クラスインスタンス」(つまりサブクラス)によって共有されますが、クラスインスタンス変数はそのクラスにのみ固有です。ただし、クラスを拡張するつもりがない場合、違いは純粋にアカデミックです。


1
それだけが違います。「共有」対「インスタンス」は、単なる継承ではありません。インスタンスゲッターを配置するS.new.s => nilと、とが取得されS.new.k => 23ます。
Andre Figueiredo

27

ソース

インスタンスメソッドの可用性

  • クラスインスタンス変数は、クラスメソッドでのみ使用でき、インスタンスメソッドでは使用できません。
  • クラス変数は、インスタンスメソッドとクラスメソッドの両方で使用できます。

遺伝性

  • クラスインスタンス変数は、継承チェーンで失われます。
  • クラス変数はそうではありません。
class Vars

  @class_ins_var = "class instance variable value"  #class instance variable
  @@class_var = "class variable value" #class  variable

  def self.class_method
    puts @class_ins_var
    puts @@class_var
  end

  def instance_method
    puts @class_ins_var
    puts @@class_var
  end
end

Vars.class_method

puts "see the difference"

obj = Vars.new

obj.instance_method

class VarsChild < Vars


end

VarsChild.class_method

15

他の人が言ったように、クラス変数は与えられたクラスとそのサブクラスの間で共有されます。クラスインスタンス変数は、正確に1つのクラスに属しています。そのサブクラスは独立しています。

なぜこの動作が存在するのですか?まあ、Rubyのすべてはオブジェクトです。クラスもです。つまり、各クラスには、それに対応するクラスClass(またはのサブクラスClass)のオブジェクトがあります。(と言うときはclass Foo、実際には定数Fooを宣言し、それにクラスオブジェクトを割り当てています。)そして、すべてのRubyオブジェクトはインスタンス変数を持つことができるため、クラスオブジェクトもインスタンス変数を持つことができます。

問題は、クラスオブジェクトのインスタンス変数が、通常クラス変数に必要な動作を実際には行わないことです。通常、スーパークラスで定義されたクラス変数をそのサブクラスと共有する必要がありますが、インスタンス変数が機能する方法ではありません。サブクラスには独自のクラスオブジェクトがあり、そのクラスオブジェクトには独自のインスタンス変数があります。したがって、彼らはあなたが望む可能性が高い動作を持つ別々のクラス変数を導入しました。

つまり、クラスインスタンス変数は、Rubyの設計の偶然のようなものです。探しているものが明確にわかっている場合を除いて、使用しないでください。


クラス変数はJavaの静的変数のようなものですか?
Kick Buttowski

3

Rubyの公式FAQ:クラス変数とクラスインスタンス変数の違いは何ですか?

主な違いは、継承に関する動作です。クラス変数はクラスとそのすべてのサブクラス間で共有されますが、クラスインスタンス変数は1つの特定のクラスにのみ属します。

クラス変数は、継承変数のコンテキスト内ではグローバル変数と見なすことができ、グローバル変数に伴うすべての問題を抱えています。たとえば、クラス変数が(誤って)そのサブクラスのいずれかによって再割り当てされ、他のすべてのクラスに影響を与える可能性があります。

class Woof

  @@sound = "woof"

  def self.sound
    @@sound
  end
end

Woof.sound  # => "woof"

class LoudWoof < Woof
  @@sound = "WOOF"
end

LoudWoof.sound  # => "WOOF"
Woof.sound      # => "WOOF" (!)

または、祖先クラスが後で再度開かれて変更され、おそらく驚くべき効果が生じる可能性があります。

class Foo

  @@var = "foo"

  def self.var
    @@var
  end
end

Foo.var  # => "foo" (as expected)

class Object
  @@var = "object"
end

Foo.var  # => "object" (!)

したがって、自分が何をしているかを正確に理解しており、この種の動作を明示的に必要としない限り、クラスインスタンス変数を使用することをお勧めします。


2

C ++の背景を持つ人は、C ++の同等の機能との比較に興味があるかもしれません。

class S
{
private: // this is not quite true, in Ruby you can still access these
  static int    k = 23;
  int           s = 15;

public:
  int get_s() { return s; }
  static int get_k() { return k; }

};

std::cerr << S::k() << "\n";

S instance;
std::cerr << instance.s() << "\n";
std::cerr << instance.k() << "\n";

ご覧のとおり、kstatic変数のようなものです。これは、クラスによって所有されていることを除いて、グローバル変数のように100%です(スコープが正しいことがスコープです)。これにより、類似した名前の変数間の衝突を簡単に回避できます。他のグローバル変数と同様に、その変数のインスタンスは1つしかなく、その変更は常にすべての人に表示されます。

一方、sはオブジェクト固有の値です。各オブジェクトには、値の独自のインスタンスがあります。C ++では、その変数にアクセスできるようにインスタンスを作成する必要があります。Rubyでは、クラス定義自体がクラスのインスタンス(JavaScriptではプロトタイプと呼ばれます)であるため、s追加のインスタンス化を行わなくてもクラスからアクセスできます。クラスインスタンスは変更できますが、の変更はs各インスタンス(タイプの各オブジェクトS)に固有です。したがって、1つを変更しても、別の値は変更されません。


1

クラスインスタンス変数はサブクラス間で共有され、シングルトンメソッドとインスタンスメソッドの両方で参照できるため、クラスインスタンス変数を利用するとすぐに便利に思えるかもしれませんが、重大な欠点があります。それらは共有されているため、サブクラスはクラスインスタンス変数の値を変更でき、基本クラスも変更の影響を受けます。これは通常、望ましくない動作です。

class C
  @@c = 'c'
  def self.c_val
    @@c
  end
end

C.c_val
 => "c" 

class D < C
end

D.instance_eval do 
  def change_c_val
    @@c = 'd'
  end
end
 => :change_c_val 

D.change_c_val
(irb):12: warning: class variable access from toplevel
 => "d" 

C.c_val
 => "d" 

Railsには、class_attributeという便利なメソッドが導入されています。その名前が示すように、サブクラスが値を継承できるクラスレベルの属性を宣言します。class_attribute値には、クラスインスタンス変数の場合と同様に、シングルトンメソッドとインスタンスメソッドの両方でアクセスできます。ただし、Railsのclass_attributeの大きな利点は、サブクラスが独自の値を変更できるため、親クラスに影響を与えないことです。

class C
  class_attribute :c
  self.c = 'c'
end

 C.c
 => "c" 

class D < C
end

D.c = 'd'
 => "d" 

 C.c
 => "c" 

よかった。これはこれまで使ったことがない。たとえばself.、属性にアクセスするたびに前に付加する必要があるのに、それは機能するようです。ドキュメントはパラメータを渡すことができると言いますが、先ほど述べたように、それは機能しないようです。cself.cdefault:class_attributeself
Dexの

「クラスインスタンス変数を使用するとすぐに役立つように見えるかもしれませんが」と言うとき、私は「クラスインスタンス変数」ではなく「クラス変数」を意味していると思います(ruby-lang.org/en/documentation/faq/8/を参照)。
キースベネット

はい、この回答は、「クラスインスタンス変数」と「クラス変数」を完全に混同します。これが問題の核心です。
stevo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.