- PR -

メソッドの引数の参照渡しはできますか?

投稿者投稿内容
miki
大ベテラン
会議室デビュー日: 2001/09/21
投稿数: 174
お住まい・勤務地: 東京都八王子市
投稿日時: 2002-03-15 09:30
>プリミティブ型は参照渡し出来ないということですね。

繰り返しになりますが、オブジェクト参照型も参照渡しできません。
Javaにはプリミティブ型とオブジェクト参照型しかないので、すべてが値渡しになります。

もし、それができたらメソッド内部から呼び出し側のオブジェクト参照型のローカル変数を書き換えることができてしまいます(オブジェクトを書き換えるのではなく、変数の内容を書きかえることに注意)。

「プログラミング言語Java第3版」に良い説明があるので引用します。
(2.6.4 パラメータの値より)

メソッドのパラメータはすべて「値渡し」です。
(略)
パラメータがオブジェクト参照の場合には、オブジェクトそのものではなく、オブジェクト参照が「値渡し」されるのに注意してください。
(略)
プログラミング言語のデザインでは、用語「参照渡し」は、関数に引数が渡される場合に、呼び出された関数が元の値への参照を得るのであり、値のコピーを得るのではないのが正しい意味です。もし、関数がそのパラメータを修正すると、引数とパラメータはメモリ上の同じ領域を使用しますので、呼び出し側の値も変更されてしまいます。
--引用終わり

この本には、図の解説と値渡しを説明したサンプルプログラムも載っています。
katsum
大ベテラン
会議室デビュー日: 2002/02/27
投稿数: 119
お住まい・勤務地: 東京都
投稿日時: 2002-03-15 14:06
>mikiさん
サンプルを作って試してみました。確かにオブジェクトも値渡しになりますね。
参考までに C++ のサンプルも作ってみました。こちらは値渡し/参照渡しどちらも可能ですね。

[Java]
class MyBoolean {
boolean b;
}

class foo {
static void main(String[] args) {
MyBoolean mb = new MyBoolean();
mb.b = true;
setFalse(mb);
System.io.println(mb.b); // true
}

static void setFalse(MyBoolean mb) { // 値渡し
mb.b = false;
}
}

[C++]
#include <stdio.h>

class MyBoolean {
public:
bool b;
};

void setFalseVal(MyBoolean mb) { // 値渡し
mb.b = false;
}

void setFalseRef(MyBoolean& mb) { // 参照渡し
mb.b = false;
}

void main(void) {
MyBoolean mb;

mb.b = true;
setFalseVal(mb);
printf("%s\\n", mb.b ? "true" : "false"); // true

mb.b = true;
setFalseRef(mb);
printf("%s\\n", mb.b ? "true" : "false"); // false
}
katsum
大ベテラン
会議室デビュー日: 2002/02/27
投稿数: 119
お住まい・勤務地: 東京都
投稿日時: 2002-03-15 16:58
上の Java に関する書き込みは間違ってました。どうも失礼しました。

上の Java コードは "System.io.println" となっているように、ここでコンパイルエラーが発生していたのに気付かず、その前にコンパイルが通った foo.class を実行していたようです。
改めて以下のコードで試してみましたら、false と表示されますね。
上の C++ の参照渡しの例と同じ動作になりました。
mikiさんがおっしゃる値渡しというのは、(C++ でいう参照渡しと同じ)この動作のことでよろしいのでしょうか?

class MyBoolean {
boolean b;
}

class foo {
static void main(String[] args) {
MyBoolean mb = new MyBoolean();
mb.b = true;
setFalse(mb);
System.out.println(mb.b); // false
}

static void setFalse(MyBoolean mb) { // 値渡し?参照渡し?
mb.b = false;
}
}
katsum
大ベテラン
会議室デビュー日: 2002/02/27
投稿数: 119
お住まい・勤務地: 東京都
投稿日時: 2002-03-15 17:39
参考までに C++ でアドレスを表示させた例も載せておきますね。
実行結果を見ると、main と setFalseRef では mb のアドレスが同じなので、参照渡しになっていることがわかります。C++屋さんは、こういうのを参照渡しと呼んでます。
C++ 屋さんなので、基準が C++ になってしまい申し訳ありません。

[C++]
#include <stdio.h>

class MyBoolean {
public:
bool b;
};

void setFalseVal(MyBoolean mb) {
mb.b = false;
printf("address of mb in setFalseVal:%p\\\\n", &mb);
}

void setFalseRef(MyBoolean& mb) {
mb.b = false;
printf("address of mb in setFalseRef:%p\\\\n", &mb);
}

void main(void) {
MyBoolean mb;
printf("address of mb in main:%p\\\\n", &mb);

mb.b = true;
setFalseVal(mb);
printf("%s\\\\n", mb.b ? "true" : "false");

mb.b = true;
setFalseRef(mb);
printf("%s\\\\n", mb.b ? "true" : "false");
}


[実行結果]
address of mb in main:0x7ffff72b
address of mb in setFalseVal:0x7ffff724
true
address of mb in setFalseRef:0x7ffff72b
false
miki
大ベテラン
会議室デビュー日: 2001/09/21
投稿数: 174
お住まい・勤務地: 東京都八王子市
投稿日時: 2002-03-15 17:43
引用:

katsumさんの書き込み (2002-03-15 14:06) より:
>mikiさん
サンプルを作って試してみました。確かにオブジェクトも値渡しになりますね。

[Java]
class MyBoolean {
boolean b;
}

class foo {
static void main(String[] args) {
MyBoolean mb = new MyBoolean();
mb.b = true;
setFalse(mb);
System.io.println(mb.b); // true
}

static void setFalse(MyBoolean mb) { // 値渡し
mb.b = false;
}
}



このJavaプログラムの出力は"false"になるはずです。
mbは同じオブジェクトを指し示していますから、setFalseはmb.bを変更します。

オブジェクトは値渡しになりません。オブジェクト参照が値渡しになるのです。
オブジェクトが値渡しされるなら、C/C++のようにオブジェクトの実体がコピーされるはずですが、そのようなことはおこりません。

Javaでは、オブジェクトはすべてHeapメモリにアロケートされ、C/C++とは異なり評価スタック上に作られることはありません。メソッド引数を渡すのにスタック上にオブジェクトの実体の代わりに、オブジェクトの参照が積まれます。C++の比喩で言えば、オブジェクト参照はすべてObject *objのようなポインタが値渡しで渡され、メソッド実行はobj->method()のように実行されます。ただ、Javaの表記上はobj.method()のように書くだけです。

次のプログラムを見てください。arg= nullの結果は、main()のmbに影響を与えないはずです。参照渡しならmainの変数mbがnullになるはずです。Javaコードと等価なC++風のコードも付けておきます。&をつかっていないでしょ。だから参照渡しではないのです。

コード:

[Java]
public class Btest{
public static void main(String[] args) {
MyBoolean mb = new MyBoolean();
setFalse(mb);
System.out.println(mb); // nullにはならない
}
static void setFalse(MyBoolean arg) {
arg= null;
}
}

[C++]
public class Btest{
public static void main(String[] args) {
MyBoolean_impl *mb = new MyBoolean_impl();
setFalse(mb);
printf("%x", mb); // NULLにはならない
}
static void setFalse(MyBoolean_impl *arg) {
arg = NULL;
}
}






[ メッセージ編集済み 編集者: miki 編集日時 2002-03-15 17:49 ]
miki
大ベテラン
会議室デビュー日: 2001/09/21
投稿数: 174
お住まい・勤務地: 東京都八王子市
投稿日時: 2002-03-15 18:04
「参照渡し」の定義が人によってまちまちなのが混乱の原因だと思います。
「参照を渡す」のと「参照渡し」は全然意味が違います。
私の定義は「プログラミング言語Java第3版」の引用に書いた通りです。
この定義はC++とも同じだと思います。

[ メッセージ編集済み 編集者: miki 編集日時 2002-03-15 18:05 ]
katsum
大ベテラン
会議室デビュー日: 2002/02/27
投稿数: 119
お住まい・勤務地: 東京都
投稿日時: 2002-03-15 19:02
>mikiさん
とてもお詳しいですね。
「オブジェクト参照が値渡しされる」とおっしゃる意味がよく分かりました。
どうもありがとうございました。

スキルアップ/キャリアアップ(JOB@IT)