C++では、関数への参照引数により、関数は参照に他の参照を参照させることができます。
int replacement = 23;
void changeNumberReference(int& reference) {
reference = replacement;
}
int main() {
int i = 1;
std::cout << "i=" << i << "\n"; // i = 1;
changeNumberReference(i);
std::cout << "i=" << i << "\n"; // i = 23;
}
同様に、関数への定数参照引数は、参照を変更しようとするとコンパイル時エラーをスローします。
void changeNumberReference(const int& reference) {
reference = replacement; // compile-time error: assignment of read-only reference 'reference'
}
現在、Javaのドキュメントでは、非プリミティブ型の関数引数は参照であると述べています。公式ドキュメントの例:
public void moveCircle(Circle circle, int deltaX, int deltaY) {
// code to move Origin of circle to x+deltaX, y+deltaY
circle.setX(circle.getX() + deltaX);
circle.setY(circle.getY() + deltaY);
// code to assign a new reference to circle
circle = new Circle(0, 0);
}
次に、円には、x = y = 0の新しいCircleオブジェクトへの参照が割り当てられます。ただし、参照が値で渡されて変更できないため、この再割り当てには永続性がありません。
私にとって、これはC++参照のようなものではありません。他の何かを参照させることができないため、通常のC++参照に似ていません。また、Javaでは、参照を変更する(実際にはそうではない)コードがコンパイルをスローしないため、C++ const参照に似ていません。 -timeエラー。
これは、C++ポインターの動作に似ています。これを使用して、ポイントされたオブジェクトの値を変更できますが、関数内でポインターの値自体を変更することはできません。また、C++ポインターの場合(C++参照ではない)と同様に、Javaでは、このような引数の値として「null」を渡すことができます。
だから私の質問は、なぜJavaは「参照」の概念を使用するのですか?それらがC++参照に似ていないことを理解する必要がありますか?それとも本当にC++参照に本当に似ていて、私はm何かが足りない?
どうして?というのも、一貫した用語は一般的には職業全体にとって良いものですが、言語デザイナーは他の言語デザイナーの言語使用を常に尊重しているわけではないためです。特に、他の言語が競合相手として認識されている場合はそうです。
しかし、実際には、どちらも「参照」の使用は非常に良い選択ではありませんでした。 C++の「参照」は、エイリアス(まったく同じエンティティの代替名)を明示的に導入するための単なる言語構成です。そもそも、新機能を単に「エイリアス」と呼んでいたことははるかに明確だったでしょう。ただし、当時の大きな困難は、ポインター(逆参照が必要)と参照(そうではない)の違いを誰もが理解できるようにすることでした。そのため、重要なのは、それが「ポインター」以外のものと呼ばれていたことです。具体的には、どの用語を使用するか。
Javaにはポインターがなく、それを誇りに思っているため、用語として「ポインター」を使用することはできませんでした。ただし、「参照」は、C++のポインターを渡すときに動作するのと同じようにかなり動作します。大きな違いは、それらに対して低レベルの低レベルの操作(キャスト、追加...)を実行できないことです。が、同一のエンティティと偶然に等しいエンティティにハンドルを渡す場合、結果はまったく同じセマンティクスになります。残念ながら、「ポインタ」という用語には、多くの否定的な低レベルの関連付けが含まれているため、Javaコミュニティによって受け入れられることはほとんどありません。
その結果、どちらの言語も2つのかなり異なるものに対して同じあいまいな用語を使用します。どちらもより具体的な名前から利益を得るかもしれませんが、どちらもすぐに置き換えられる可能性はほとんどありません。自然言語も時々イライラすることがあります!
参照とは、別のものを参照するものです。さまざまな言語が、「参照」という単語のより具体的な意味を、通常「すべての悪い側面のないポインタのようなもの」に帰している。 C++は、JavaまたはPerlのように、特定の意味を規定しています。
C++では、参照はエイリアスに似ています(ポインタを介して実装できます)。 pass by reference またはout arguments。
Javaでは、参照はポインタです。ただし、これは言語の具体化された概念ではありません。すべてのオブジェクトは参照であり、数値のようなプリミティブ型はそうではありません。 Javaにはポインター演算がなく、具体化されたポインターがないため、彼らは「ポインター」と言いたくありませんが、Object
を引数として渡すと、オブジェクトはコピーされません。これも参照渡しではなく、共有渡し。
それは主にALGOL 68に戻り、一部はCがポインターを定義する方法に対する反応に戻ります。
ALGOL 68は、参照と呼ばれる概念を定義しました。これは、Pascalのポインタ(一例として)とほとんど同じでした。これは、NILまたは指定されたタイプの他のセルのアドレスを含むセルでした。参照に割り当てることができるため、参照は一度に1つのセルを参照し、再割り当て後に別のセルを参照できます。 notを実行しましたが、CまたはC++のポインタ演算に類似したものはすべてサポートしています。
ただし、少なくともALGOL 68で定義されているように、参照はかなりクリーンな概念であり、実際に使用するにはかなり扱いにくいものでした。ほとんどの変数の定義は実際には参照でしたので、完全に手に負えなくなるのを防ぐために省略表記を定義しましたが、とにかく簡単な使い方をすれば、とにかくすぐに不器用になる可能性があります。
たとえば、_INT j := 7;
_のような宣言は、実際にはコンパイラによって_REF INT j = NEW LOC INT := 7
_のような宣言として扱われました。したがって、ALGOL 68で宣言したのは通常参照であり、ヒープ上に割り当てられた何かを参照するように初期化され、それは指定された値を含むように(オプションで)初期化されました。ただし、Javaとは異なり、foo bar = new foo();
のようなものを常に使用したり、「私たちのポインターはポインターではない」とfibに伝えたりする代わりに、少なくともJavaの正しい構文を維持できるようにしました。
Pascalとその子孫のほとんど(直接および...霊的)は、ALGOL 68参照概念の名前を「ポインター」に変更しましたが、概念自体は基本的に同じに保ちました:ポインターは、nil
またはヒープに割り当てたもののアドレス(つまり、少なくとも元々はJensenとWirthによって定義されたように、 "address-of"演算子がなかったため、ポインターが通常定義された変数を参照する方法がありませんでした)。 「ポインタ」であるにもかかわらず、ポインタ演算はサポートされていませんでした。
CとC++はそれに少しひねりを加えました。まず、それらはdoにアドレス演算子があります。そのため、ポインターはヒープ上に割り当てられたものだけでなく、どのように割り当てられているかに関係なく、任意の変数を参照できます。第2に、それらはポインターの算術を定義し、配列の添え字を基本的にポインター算術の単なる省略表記として定義するため、ほとんどのCおよびC++では、ポインター算術は(不可避の隣に)遍在しています。
Javaが発明されたとき、Sunは明らかに「Javaにはポインタがない」というのは、「Javaポインタですが、これらのポインタはほとんどがCではなくPascalのようなものです。 "彼らは"ポインタ "は受け入れられない用語であると判断したため、他のものが必要であり、その参照は微妙であるにもかかわらず(そして、いくつかのケースではそれほど微妙ではありません)ALGOL 68リファレンスとは異なります。
多少異なりますが、C++はほぼ同じ問題に悩まされていました。Wordの「ポインター」は既知であり、理解されていたため、追加している別のWordに別のWordが必要でした。人々が「ポインタ」を意味すると理解したこととは少し異なります。そのため、ALGOL 68のリファレンスとは著しく異なりますが、「リファレンス」という用語も再利用しました。
「参照型」の概念は一般的なものであり、「値型」とは対照的に、ポインターと参照(C++の観点から)の両方を表現できます。 Javaの参照は、->
と*
の逆参照による構文上のオーバーヘッドがない実際のポインタです。 C++でのJVMの実装を見ると、それらは実際には単なるポインタです。また、C#にはJavaと同様の参照の概念がありますが、関数パラメーターにポインターと 'ref'修飾子があるため、C++の&
のように、参照によって値型を渡すことができ、コピーを回避できます。