先に結論
- rubyの代入はオブジェクトの参照値(ポインタのようなもの)が代入先に格納される
- もし、格納元が参照値の場合、同一の参照値が代入先に格納される(参照の参照にはならない)
- イメージとしては、C++のポインタの値の代入に近い
文字列"aaa"
を変数a
に代入する場合
a = "aaa"
上記の場合、以下の処理が実行される
Stringオブジェクト"aaa"
が生成される- 変数
a
にStringオブジェクト"aaa"
の参照値が格納される
その変数a
を別の変数b
に代入するばあい
a = "aaa" b = a
上記の場合、
- 変数
a
に格納されているStringオブジェクト"aaa"
の参照値が、変数b
に 格納される
同じオブジェクトが参照されていることが object_id
を比較すると分かる
a = "aaa" b = a a.object_id == b.object_id # true
変数b
に破壊的メソッドsub!
を実行した場合
a = "aaa" b = a b.sub!(/a/, 'b')
この場合、変数 a
も b
も同じオブジェクトを参照しているため、変数 a
の表示も変わる
a = "aaa" b = a b.sub!(/a/, 'b') p b # "bbb" p a # "bbb"
変数b
に別の文字列 "bbb"
を代入する場合
a = "aaa" b = a b = "bbb"
このばあい、変数b
には新たに生成されたStringオブジェクト"bbb"
の参照値が格納され、変数a
とは参照先が別になる
変数b
と変数a
の参照先が異なることが object_id
を比較すると分かる
a = "aaa" b = a b = "bbb" a.object_id != b.object_id # true
ぎゃくに変数a
に別の文字列 "bbb"
を代入する場合
a = "aaa" b = a a = "bbb"
このばあい、変数a
にはStringオブジェクト"bbb"
の参照値が格納される
しかし、変数b
はStringオブジェクト"aaa"の参照値が格納されたままのため、文字列 "aaa"
が表示される
a = "aaa" b = a a = "bbb" p b # "aaa" a.object_id != b.object_id # true
上記のことから rubyの変数には参照ではなく、参照値が格納されていることが分かる
なんで?
もし、a="aaa"
が C++などで言う「参照」であるならば
b=a; b="bbb"
としたときに Stringオブジェクト"aaa"
の内容が "bbb"
に変わるはず
C++での参照
#include <iostream> int main(void){ int x = 100; int& xr = x; xr = 200; // xの参照である xr の値を変更するとxの値も変更される std::cout << "x=" << x << "\n"; // 200 return 0; }
どちらかというと、C++でいうポインタの値の代入のイメージに近い
しかし、rubyではポインタは無いためJavaに倣って「参照値の代入」という言葉を選んだ
C++でのポインタの値の代入
#include <iostream> int main(void){ int a = 100; int* ap = &a; //apにaのポインタの値を格納 std::cout << "ap=" << ap << "\n"; //aのポインタの値(0x7fff4fe752b8) int* bp = ap; //aのポインタの値をbpに格納(rubyで言う a=b) std::cout << "bp=" << bp << "\n"; //ap == bp int b = 999; bp = &b; //bpにbのポインタの値を格納(rubyでいう b=999) std::cout << "bp=" << bp << "\n"; // ap != bp(999) std::cout << "a=" << a << "\n"; // 当然 a の値は変更されない(100) return 0; }