質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

4回答

5729閲覧

クラス変数とインスタンス変数の使いどころ

terate

総合スコア103

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

3クリップ

投稿2016/07/16 05:24

クラス変数とインスタンス変数の違いについて調べて下記で学びました。

http://qiita.com/wat-aro/items/f37d7b1a2e341d06966e

http://qiita.com/mogulla3/items/cd4d6e188c34c6819709

ですが、いまいち使いどころというか、どのように使い分けするのがより無駄なく機能的なのかがイメージつかなく、どちらでもたいして労力変わらず同じものを呼びだせてしまう理解から抜け出せません。

それぞれの使いどころの具体的な説明をいただけましたら幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

クラス変数とインスタンス変数は性質が全く異なるモノです。実例を見ながら、動作を説明しましょう。

ちょっとしたTODOツールを作ろうと思いました。全体はまだですが、とりあえずTodoクラスを作って、テキストメッセージや終わったかどうかを管理しようと思います。

Ruby

1class Todo 2 def initialize(text) 3 @text = text 4 @done = false 5 end 6 7 def text 8 @text 9 end 10 11 def do! 12 if !@done 13 @done = true 14 end 15 end 16 17 def done? 18 @done 19 end 20end 21 22# 二つのTODOを作成 23todo1 = Todo.new('質問する。') 24todo2 = Todo.new('ベストアンサーを選ぶ。') 25 26# 一つ目はもう実行した 27todo1.do! 28 29# 一つ目について終わっているのか? 30if todo1.done? 31 puts "TODO「#{todo1.text}」は終わりました。" 32else 33 puts "TODO「#{todo1.text}」はまだやってません。" 34end 35 36# 二つ目について終わっているのか? 37if todo2.done? 38 puts "TODO「#{todo2.text}」は終わりました。" 39else 40 puts "TODO「#{todo2.text}」はまだやってません。" 41end

出力結果

TODO「質問する。」は終わりました。 TODO「ベストアンサーを選ぶ。」はまだやってません。

二つのTODOは独立しています。なので、一つ目だけ終わらせても、二つ目は終わってません。さて、もし、インスタンス変数@doneがクラス変数@@doneだったらどうなるのでしょうか?

Ruby

1class Todo 2 def initialize(text) 3 @text = text 4 @@done = false 5 end 6 7 def text 8 @text 9 end 10 11 def do! 12 if !@@done 13 @@done = true 14 end 15 end 16 17 def done? 18 @@done 19 end 20end 21 22# 二つのTODOを作成 23todo1 = Todo.new('質問する。') 24todo2 = Todo.new('ベストアンサーを選ぶ。') 25 26# 一つ目はもう実行した 27todo1.do! 28 29# 一つ目について終わっているのか? 30if todo1.done? 31 puts "TODO「#{todo1.text}」は終わりました。" 32else 33 puts "TODO「#{todo1.text}」はまだやってません。" 34end 35 36# 二つ目について終わっているのか? 37if todo2.done? 38 puts "TODO「#{todo2.text}」は終わりました。" 39else 40 puts "TODO「#{todo2.text}」はまだやってません。" 41end

出力結果

TODO「質問する。」は終わりました。 TODO「ベストアンサーを選ぶ。」は終わりました。

あれ、一つ目しか終わってないのに、二つ目も終わってしまいました。なぜそうなるのか、一番目のコードと何が違うのかを見ましょう。

一番目のコードでは、todo1は終わったかどうかを管理するために@doneを持っています。そして、todo2も終わったかどうかを管理するために@doneを持っています。@doneはインスタンス変数なので、この二つは別のモノです。つまり、お互いに独立しており、片方を変えても、もう片方には一切影響はありません。

しかし、クラス変数にすると事情が変わります。二番目のコードでは、todo1@@doneを持ち、todo2@@doneを持っています。ここまでは同じように見えますが、この二つの@@doneはクラス変数なので同じモノです。先ほどとは全く逆で、お互いは同じモノを示しており、片方を変えれば、もう片方も変わっています。

こうして、二番目のコードでは、todo1だけ終わらせたつもりが、todo2まで終わっていたという、使えないコードになってしまいました。

では、クラス変数はどういうときに使うのでしょうか?Todoの数や終わった数をカウントして、完了率を出そうと思います。正常に動く一番目のコードを次のように拡張してみました。

Ruby

1class Todo 2 def initialize(text) 3 @text = text 4 @done = false 5 @total_todo_count ||= 0 6 @total_todo_count += 1 7 @done_todo_count ||= 0 8 end 9 10 def text 11 @text 12 end 13 14 def do! 15 if !@done 16 @done = true 17 @done_todo_count += 1 18 end 19 end 20 21 def done? 22 @done 23 end 24 25 def done_persent 26 100 * @done_todo_count / @total_todo_count 27 end 28end 29 30# 二つのTODOを作成 31todo1 = Todo.new('質問する。') 32todo2 = Todo.new('ベストアンサーを選ぶ。') 33 34# 一つ目はもう実行した 35todo1.do! 36 37# 一つ目について終わっているのか? 38if todo1.done? 39 puts "TODO「#{todo1.text}」は終わりました。" 40else 41 puts "TODO「#{todo1.text}」はまだやってません。" 42end 43puts "TODOの完了率は#{todo1.done_persent}%です。" 44 45# 二つ目について終わっているのか? 46if todo2.done? 47 puts "TODO「#{todo2.text}」は終わりました。" 48else 49 puts "TODO「#{todo2.text}」はまだやってません。" 50end 51puts "TODOの完了率は#{todo2.done_persent}%です。"

出力結果

TODO「質問する。」は終わりました。 TODOの完了率は100%です。 TODO「ベストアンサーを選ぶ。」はまだやってません。 TODOの完了率は0%です。

あれ、TODOは二つあって、一つしか終わってないから、完了率は50%になるはずですよね?何が悪かったのでしょうか。とりあえず、新しく追加した@done_todo_count@total_todo_countをクラス変数に変えてみましょう。

Ruby

1class Todo 2 def initialize(text) 3 @text = text 4 @done = false 5 @@total_todo_count ||= 0 6 @@total_todo_count += 1 7 @@done_todo_count ||= 0 8 end 9 10 def text 11 @text 12 end 13 14 def do! 15 if !@done 16 @done = true 17 @@done_todo_count += 1 18 end 19 end 20 21 def done? 22 @done 23 end 24 25 def done_persent 26 100 * @@done_todo_count / @@total_todo_count 27 end 28end 29 30# 二つのTODOを作成 31todo1 = Todo.new('質問する。') 32todo2 = Todo.new('ベストアンサーを選ぶ。') 33 34# 一つ目はもう実行した 35todo1.do! 36 37# 一つ目について終わっているのか? 38if todo1.done? 39 puts "TODO「#{todo1.text}」は終わりました。" 40else 41 puts "TODO「#{todo1.text}」はまだやってません。" 42end 43puts "TODOの完了率は#{todo1.done_persent}%です。" 44 45# 二つ目について終わっているのか? 46if todo2.done? 47 puts "TODO「#{todo2.text}」は終わりました。" 48else 49 puts "TODO「#{todo2.text}」はまだやってません。" 50end 51puts "TODOの完了率は#{todo2.done_persent}%です。"

出力結果

TODO「質問する。」は終わりました。 TODOの完了率は50%です。 TODO「ベストアンサーを選ぶ。」はまだやってません。 TODOの完了率は50%です。

今度はちゃんと50%になりました。なぜ、うまくいったのでしょうか?

三番目のコードでは@total_todo_count @done_todo_count はインスタンス変数でした。ですので、todo1todo2それぞれが持っている@total_todo_count @done_todo_count は独立しています。todo1が終わったからとカウントを追加してもtodo1の中のカウントしか増えません。そもそも、最初の全体数@total_todo_countも独立しているので、互いにずっと1のままです。

そこでクラス変数の出番です。クラス変数@@total_todo_count @@done_todo_count とすることで、todo1todo2から共通で使えるようになりました。@@total_todo_countは最初のinitializeで正常にカウントされ、2になります。その後、todo1が終わりましたので、@@done_todo_count1になります。これはtodo1がみている@@total_todo_count @@done_todo_count todo2がみている@@total_todo_count @@done_todo_count 、両方がそうなっています。なぜなら、それらは同じモノであり、@@total_todo_count @@done_todo_count は、todo1todo2が共通で使っているからです。

といことで、クラス変数とインスタンス変数は全く異なる性質のモノですので、使い分けどころか、どちらを使うという選択はできません。インスタンス変数をクラス変数に、クラス変数をインスタンス変数に変えれば、おかしな動きになってしまいます。

そうそう、Rubyにはクラスのインスタンス変数というモノがありますが、それはまた異なった性質のモノです。コレまで話していた内容はクラス変数とインスタンス変数の違いであり、クラスのインスタンス変数は含まれていませんので、ご注意ください。もし、そちらをお聞きになりたいのであれば、質問を編集するなり,再度質問をするなりしてください。

投稿2016/07/16 12:54

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terate

2016/07/21 14:44

こんなにたくさんわかりやすくご丁寧にありがとうございます!おかげでとてもよくわかりました!!
guest

0

Rubyに限らず一般的な話ですが、クラスは定義した型です。
インスタンスはクラスをもとに生成したオブジェクトです。
なので、クラス変数は、クラスに紐づいている変数
インスタンス変数は、インスタンスに紐づいている変数です。

例えば、「人間」というクラスがあったとして、あなたや私は「人間」クラスのインスタンスです。
※人間という型をもとに作られたそれぞれのオブジェクト(生きている人)です。

人間のクラス変数として考えられるのは、例えば目の数とか人間全体の人数とかです。
目の数はあなたも私も普通2つですよね、なので情報として一つ持てばいいので普通はクラスに1か所だけ持ちます。(定数でいいとかいうのはおいておいて)

人間のインスタンス変数として考えられるのは、人間ごとに違うもの、
例えば身長や体重などです。(人間それぞれで違う情報)

これは、どのようなクラスでも同じ、例えば車クラスのインスタンスは、レクサスとかキューブとか
そんな感じで、型全体で共有するのはクラス変数、インスタンスそれぞれで持つものはインスタンス変数として使用します。

投稿2016/07/16 07:30

Mr_Roboto

総合スコア2208

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terate

2016/07/21 14:44

なるほどですね、勉強になります!ありがとうございます!!
guest

0

基本的に、Rubyにおいてクラス変数は(よほどそれを必要とする理由がない限り)使うべきでないと考えます。クラス変数は、クラス自体・インスタンス間で共有されるので、思わぬところで変更が入って、デバッグしにくくなる原因となります。

クラスメソッドで使う値などは、クラスインスタンス変数(クラス内での単なるインスタンス変数)を使えば充分に書けます。そして、それはクラスメソッド外からはうっかり書き換えられないので、スコープ的にも適切な場合が多いです。

投稿2016/07/16 05:39

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terate

2016/07/21 14:43

すごく勉強になります!ありがとうございます!!
guest

0

参考:

...
インスタンス変数とクラス変数の最も大きな違いは、インスタンス毎に値が固有であるか否かにあります。
...

-インスタンス変数とクラスインスタンス変数の違い http://blog.livedoor.jp/sasata299/archives/51676591.html

...
インスタンス変数はオブジェクトに属しているので、オブジェクト毎に値を持ちます。
...
クラスインスタンス変数はクラスに属しているのでクラスに対して一つだけ値を持ちます。
...

投稿2016/07/16 05:38

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terate

2016/07/16 08:59

すみません。。3番目のURL内のテラテイルで過去質問に対する回答である、 ”そのクラスから作られたオブジェクト全てで” と、 ”そのクラスで作られたオブジェクト内で” の違い、意味がちょっとわからないのですが教えていただくことは出来ませんでしょうか?お手数をおかけして申し訳ありません。
katoy

2016/07/16 14:32

”そのクラスで作られたオブジェクト内で共有される変数" は ”そのクラスで作られたオブジェクトごとに管理される変数" と理解したほうがよいと思います。
terate

2016/07/21 14:43

なるほど!大変勉強になりました!ありがとうございます!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問