루비 클래스 인스턴스 변수와 클래스 변수


179

" Ruby 인스턴스 변수는 언제 설정됩니까? "를 읽지 만 클래스 인스턴스 변수를 사용할 때는 두 가지가 있습니다.

클래스 변수는 클래스의 모든 객체가 공유하며 인스턴스 변수는 하나의 객체에 속합니다. 클래스 변수가 있다면 클래스 인스턴스 변수를 사용할 여지가 충분하지 않습니다.

누군가이 둘의 차이점과 사용시기를 설명 할 수 있습니까?

코드 예제는 다음과 같습니다.

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인스턴스 객체에서 쓸 필요가 없으며 편리 하다면 클래스 계층 전체에서 자동 공유를 얻을 수 있습니다.


이를 인스턴스의 인스턴스 변수를 다루는 단일 예제로 병합합니다.

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의 차이점은 무엇입니까?
cyborg

1
@cyborg 는 현재 범위 self.things의 메소드 things를 참조했습니다 (클래스의 인스턴스의 경우 인스턴스의 메소드가됩니다). 여기서 현재 범위의 클래스의 메소드를 self.class.things참조 things합니다 (클래스의 인스턴스의 경우) 클래스 방법).
graffzon

아름다운 설명.
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

다른 사람들이 말했듯이 클래스 변수는 주어진 클래스와 그 서브 클래스 사이에서 공유됩니다. 클래스 인스턴스 변수는 정확히 하나의 클래스에 속합니다. 서브 클래스는 분리되어 있습니다.

왜이 동작이 존재합니까? 글쎄, 루비의 모든 것은 심지어 클래스조차도 객체입니다. 즉, 각 클래스에는 해당 클래스의 객체 Class(또는 하위 클래스 Class)가 있습니다. (라고 말하면 class Foo실제로 상수를 선언하고 Foo클래스 객체를 할당합니다.) 모든 Ruby 객체는 인스턴스 변수를 가질 수 있으므로 클래스 객체도 인스턴스 변수를 가질 수 있습니다.

문제는 클래스 객체의 인스턴스 변수가 일반적으로 클래스 변수가 원하는 방식으로 작동하지 않는다는 것입니다. 일반적으로 수퍼 클래스에 정의 된 클래스 변수가 서브 클래스와 공유되기를 원하지만, 이는 인스턴스 변수가 작동하는 방식이 아닙니다. 그래서 그들은 당신이 원하는 행동으로 별도의 클래스 변수를 도입했습니다.

다시 말해, 클래스 인스턴스 변수는 일종의 루비 디자인 사고입니다. 그들이 당신이 찾고있는 것을 구체적으로 알지 못한다면 아마 사용해서는 안됩니다.


클래스 변수는 Java의 정적 변수와 비슷합니까?
Kick Buttowski

3

공식 Ruby FAQ : 클래스 변수와 클래스 인스턴스 변수의 차이점은 무엇입니까?

주요 차이점은 상속과 관련된 동작입니다. 클래스 변수는 클래스와 모든 하위 클래스간에 공유되는 반면 클래스 인스턴스 변수는 하나의 특정 클래스에만 속합니다.

어떤 식 으로든 클래스 변수는 전역 변수와 함께 발생하는 모든 문제와 함께 상속 계층 구조의 컨텍스트 내에서 전역 변수로 볼 수 있습니다. 예를 들어, 클래스 변수는 다른 모든 클래스에 영향을주는 하위 클래스 중 하나에 의해 실수로 재 지정 될 수 있습니다.

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";

보시다시피 k, static비슷한 변수입니다. 이것은 클래스 가 소유 하고 있다는 것을 제외하고는 전역 변수와 100 %입니다 ( 정확한 범위 ). 이렇게하면 비슷한 이름의 변수 사이의 충돌을 피하기가 더 쉽습니다. 다른 전역 변수와 마찬가지로 해당 변수의 인스턴스는 하나 뿐이며 수정하면 항상 모든 사람이 볼 수 있습니다.

한편, s객체 고유의 값이다. 각 객체에는 고유 한 값 인스턴스가 있습니다. C ++에서는 해당 변수에 액세스 할 수있는 인스턴스를 작성해야합니다. Ruby에서 클래스 정의 자체는 클래스의 인스턴스 (JavaScript에서는 프로토 타입이라고 함)이므로 s추가 인스턴스화없이 클래스에서 액세스 할 수 있습니다 . 클래스 인스턴스는 수정할 수 있지만 수정은 s각 인스턴스마다 고유합니다 (각 유형의 객체 S). 따라서 하나를 수정해도 다른 값은 변경되지 않습니다.


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/ 참조) . )
Keith Bennett

예,이 답변은 "클래스 인스턴스 변수"와 "클래스 변수"를 완전히 혼동합니다. 이는 질문의 요점입니다.
스테보
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.