疑問メモ: 値渡し、参照渡し、Java、C
Javaのオブジェクト参照は、Cのポインタをラップしたようなモノである、という理解なのだけれど、要は何なのだろう?
- | Java | C | C++ |
---|---|---|---|
仮引数が基本データ型(Java) or 変数のみ(C/C++) | 値渡し | 値渡し | 値渡し |
仮引数が配列かオブジェクト(Java) or 変数*(C/C++) | (オブジェクト参照の)値渡し | (ポインタの)値渡し | (ポインタの)値渡し |
仮引数が変数&(C++) | - | - | 参照渡し |
ちょっと整理する。
CとJavaの値渡し
Javaでは、メソッドの引数が基本データ型のとき、実引数の値のコピーが仮引数に渡される。これは、C言語で関数の仮引数に何もつけないときと同じ動きになる。
また、C言語では、関数の仮引数がポインタのときに、ポインタが値渡しされる。これを参照渡しと呼ぶのは誤りである。アドレスがコピーされて渡されるだけなので、値渡しである。
Javaでメソッドの引数がオブジェクトのときも、オブジェクト参照が値渡しされる。これも参照渡しではない。
C++の参照渡し
C++には本物の参照渡しがある。参照渡しでは、実引数のアドレスが関数に渡され、仮引数と同じアドレスに配置される。実引数と仮引数が同じ記憶域を共有する。
- 関数間の独立性を保ちづらい
- 再帰を書くのが困難
- 呼び出し側のコードを見ただけでは、渡しているのが値なのか参照なのか判別できない
とか、いろいろある。
Javaの「オブジェクト参照の値渡し」のサンプル
古い記事にいいカンジのサンプルがあった。
ちょっと改造する。
import java.awt.Point; public class RefSample { public static void tricky(Point arg1, Point arg2) { arg1.x = 100; arg1.y = 100; // スワップを試みる Point temp = arg1; arg1 = arg2; arg2 = temp; // 値に触れることを確認 arg2.x += 50; System.out.println("arg1 X: " + arg1.x + " Y: " + arg1.y); System.out.println("arg2 X: " + arg2.x + " Y: " + arg2.y); System.out.println(); } public static void main(String[] args) { Point pnt1 = new Point(0, 0); Point pnt2 = new Point(0, 0); System.out.println("pnt1 X: " + pnt1.x + " Y: " + pnt1.y); System.out.println("pnt2 X: " + pnt2.x + " Y: " + pnt2.y); System.out.println(); tricky(pnt1, pnt2); System.out.println("pnt1 X: " + pnt1.x + " Y:" + pnt1.y); System.out.println("pnt2 X: " + pnt2.x + " Y: " + pnt2.y); } }
実行結果。
pnt1 X: 0 Y: 0 pnt2 X: 0 Y: 0 arg1 X: 0 Y: 0 arg2 X: 150 Y: 100 pnt1 X: 150 Y:100 pnt2 X: 0 Y: 0
もし、JavaにC++のような参照渡しがあれば、スワップが実現してしまい、最後の出力は次のようになるんかな。
pnt1 X: 0 Y: 0 pnt2 X: 150 Y:100
残った疑問
Javaのオブジェクト参照とは、メモリ上のどのような実体なのか?
オブジェクト参照は、アドレスのように、それ自体を足したり引いたりすることはできません。つまり、アドレスを言語機構でラップしたナニカです。
で、それ、なんなん・・・? というのが、今日の残った疑問だったり。