クラスメソッドとクラス変数
昨日まで調べていた ruby 1.9 の新機能、クラスローカルインスタンス変数が、最新のリビジョン(11813)では取り除かれている。
どうやら、この辺りの事情のようだ。
ごもっともなご意見である。
というわけで、今日はターゲットを変更して、クラス変数を調べてみることにした。
クラス変数
クラス変数は、クラスのオブジェクトから共通に参照できる変数だ。C++ の static メンバ変数と同じような働きをする。
ところで、話は変わるが、C++ の static というキーワードは文脈によって全然違う意味を持つような。クラス定義内で使われた場合は、クラスメソッドかクラス変数を意味するが、関数内で使われた場合は関数スコープの静的変数を意味する。トップレベルで使われた場合は、ファイルスコープの関数定義または変数定義となる。
よくもまあいろいろと使い回したもんだ、と思ったが、もしかしたら、関数と変数に対して、スコープとメモリの確保方法の2種類のパラメーターを決めてるだけか。たぶんそうに違いない。この「原理」を昔発見して、すごく嬉しかったのを覚えているような、ないような。
以上は余談。
Ruby のインスタンス変数はオブジェクトの中にあるが、クラス変数はどこにあるんだろうか?
「クラスの中」と思った人、正解です。
Ruby ではクラスもオブジェクトなので、そのオブジェクトの中に、クラス変数は存在する。それもオブジェクトとしてのクラスの、インスタンス変数が格納されるのと同じテーブルに格納される。
ということは、クラス変数と、クラスのインスタンス変数は衝突しないんだろうか?
Ruby ではクラス変数とインスタンス変数の表記が違う。 (@@a vs. @a) そして、変数をテーブルに格納するときには @ を含めた名前をキーにして格納するので、同じテーブルを使っても名前の衝突は起こらないというのが解答。なかなかうまくできている。
この辺りの実装の説明は、RHG 第6章の「クラス変数」が詳しい。
クラスメソッドとクラス変数
クラス変数はクラスの変数、と言い切ってしまうと、とてもシンプルに聞こえる。ところがクラスメソッドがからんでくると、なかなか興味深いことになる。
下の3つのメソッド定義は、どれもクラス A のクラスメソッドを作成する。
class A @@a = "Hello" def A.test_in puts @@a end end def A.test_out puts @@a end class<<A def test_singleton puts @@a end end
どのメソッドも A.test_XXX という形式で呼ぶことができるのだが、その結果がメソッドごと、また ruby のバージョンごとに微妙に違う。
A.test_in # 1.8 -> Hello # 1.9 (r11842) -> Hello A.test_out # 1.8 -> エラー uninitialized class variable @@a in Object (NameError) # 1.9 -> 警告 class variable access from toplevel & エラー uninitialized class variable @@a in Object (NameError) A.test_singleton # 1.8 -> 警告 class variable access from toplevel singleton method & エラー uninitialized class variable @@a in Object (NameError) # 1.9 -> Hello
どれも同じように、クラスメソッドからクラス変数を参照しているのだが、結果は見てのとおりかなり違う。
何でこのようなことが起こるんだろう?これを調べるために ruby のコードを追ってみた。結果はまた後ほど。