なぜC ++とJavaの両方が「参照」の概念を使用しますが、同じ意味では使用しないのですか?


26

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参照に似ていません時間エラー。

これは、動作がC ++ポインターに似ています。これを使用して、ポイントされたオブジェクトの値を変更できますが、関数内でポインターの値自体を変更することはできません。また、C ++ポインター(C ++参照ではない)と同様に、Javaでは、そのような引数の値として「null」を渡すことができます。

だから私の質問は:なぜJavaは「参照」の概念を使用するのですか?C ++の参照に似ていないことを理解する必要がありますか?または、実際にC ++の参照に本当に似ており、何かが欠けていますか?


10
最初のC ++の例では、「何か他のものを指す」参照を作成していないことに注意してください。代わりに、参照が指す変数の値を変更しています。これは、Javaの参照と呼ばれるオブジェクトの状態を突然変異と概念的に類似している
シオン

@Xionうん、私はあなたのコメントを読んだので、あなたの言うことを理解し、あなたの言うことの98%に同意します:)Javaの問題は、概念的にオブジェクトのすべての状態にアクセスする必要があることです参照を他の参照の値に設定できるようにすることで、C ++が単に行うことを実現します。
シヴァンドラゴン14年

回答:


48

どうして?なぜなら、一貫した用語は一般的に職業全体に適していますが、言語設計者は他の言語設計者の言語使用を常に尊重するとは限らないためです。

しかし、実際には、どちらの「参照」の使用も非常に良い選択ではありませんでした。C ++の「参照」は、エイリアス(まったく同じエンティティの代替名)を明示的に導入するための単なる言語構成体です。そもそも新機能を単に「エイリアス」と呼んでいたことは、もっとはっきりしていたでしょう。ただし、当時の大きな難題は、ポインター(参照解除が必要)と参照(参照しない)の違いを全員に理解させることでした。そのため、重要なことは、「ポインター」以外のものと呼ばれることでした。より具体的にはどの用語を使用するか。

Javaにはポインターがなく、誇りに思っているので、用語として「ポインター」を使用することは選択肢になりませんでした。ただし、C ++のポインターを渡す際に行う「参照」は、C ++のポインターと同じようにかなり動作します。大きな違いは、それらに対して厄介な低レベル操作(キャスト、追加など)を実行できないことです。 、ただし、同一のエンティティと単に等しいエンティティにハンドルを渡すと、まったく同じセマンティクスになります。残念なことに、「ポインタ」という用語には非常に多くのネガティブな低レベルの関連付けが含まれているため、Javaコミュニティに受け入れられることはありそうにありません。

その結果、両方の言語が2つのかなり異なる用語に対して同じあいまいな用語を使用し、どちらもより具体的な名前から利益を得られる可能性がありますが、どちらもすぐには置き換えられない可能性があります。自然言語も時々イライラすることがあります!


7
おかげで、C ++参照を「エイリアス」(同じ値/インスタンス)として、Java参照を「厄介な低レベル操作(キャスト、追加...)のないポインター」として考えることで、本当にわかりやすくなりました。
シヴァンドラゴン14年

14
Javaにはポインターがなく、誇りに思っているので、用語として「ポインター」を使用することは選択肢になりませんでした。どの程度NullPointerExceptionかJLSは「ポインタ」という用語を使用するという事実?
トバイアスブラント14年

7
@TobiasBrandt:NullPointerExceptionはポインターではありませんが、下位レベルでNULLポインターが渡された/逆参照されたことを示す例外です。使用するPointer例外の一部として、どちらかといえば、彼らはhave.The JLSは厄介なイメージに追加している JavaのVMは、主にそれらを使用する言語で書かれていたので、ポインタを言及します。内部の仕組みを説明せずに、言語仕様を正確に説明することはできません
エリアスヴァンOotegem

3
@TobiasBrandt:Javaはあらゆる場所でポインターを使用します。ヒープオブジェクトは、すべての実用的な目的のためにポインターであるものを介して参照されます。Javaがポインター型の操作をプログラマーに公開しないというだけです。
ジョンボード14年

2
Java / .NET参照には「オブジェクトID」という用語を使用します。ビットレベルでは、このようなものは通常、ポインターに似たものとして実装されますが、そうである必要はありません。重要なのは、フレームワーク/ vmがオブジェクトを識別するために必要なあらゆる種類の情報がオブジェクトIDに含まれていることです。
supercat

9

参照とは、別のものを参照するものです。さまざまな言語は、「参照」という言葉、通常は「すべての悪い側面のないポインターのようなもの」という言葉に、より具体的な意味を与えています。C ++は、JavaまたはPerlと同様に、特定の意味を与えます。

C ++では、参照はエイリアス(ポインタを介して実装できる)に似ています。これにより、参照渡しまたは出力引数渡しが可能になります

Javaでは、参照はポインターです。ただし、これは言語の具体化された概念ではありません。すべてのオブジェクトは参照であり、数値のようなプリミティブ型はそうではありません。Javaにはポインター演算や具体化されたポインターがないため、「ポインター」とは言いたくありませんがObject、引数として渡すとオブジェクトがコピーされないことを明確にしたいのです。これも参照渡しではありませんが、共有渡しのようなものです。


6

これは主にAlgol 68にまで遡り、一部はCがポインターを定義する方法に対する反論に基づいています。

Algol 68は、リファレンスと呼ばれる概念を定義しました。これは、Pascalのポインターとほぼ同じ(一例)です。これは、NILまたは特定のタイプのその他のセルのアドレスのいずれかを含むセルでした。参照に割り当てることができるため、参照は一度に1つのセルを参照でき、再割り当て後は別のセルを参照できます。ただし、CまたはC ++ポインター演算に類似したものはサポートしていませんでした

しかし、少なくともAlgol 68が定義したように、参照は実際に使用するにはかなり不器用だったかなりきれいな概念でした。ほとんどの変数定義は実際には参照であるため、完全に手に負えなくなるのを防ぐために略記法を定義しました。

たとえば、のような宣言INT j := 7;は、実際にはコンパイラによってのような宣言として扱われましたREF INT j = NEW LOC INT := 7。したがって、Algol 68で宣言したのは通常参照であり、ヒープに割り当てられたものを参照するために初期化され、指定された値を含むように(オプションで)初期化されました。しかし、Javaとは異なり、彼らは少なくともfoo bar = new foo();「私たちのポインターはポインターではない」というようなことを常にしたり、fibsに伝えようとする代わりに、そのための健全な構文を維持できるようにしました。

パスカルとその子孫のほとんど(直接的および...精神的の両方)は、「ポインタ」にアルゴル68参照概念の名前を変更しますが、同じ基本的概念そのものを保持:ポインタがどちらか開催されたことを変数だったnil、またはあなたが割り当てられた何かのアドレスヒープ上(つまり、少なくともJensenとWirthによって最初に定義されたように、 "address-of"演算子がなかったため、ポインターが通常定義された変数を参照する方法がありませんでした)。「ポインター」であるにもかかわらず、ポインター演算はサポートされていませんでした。

CとC ++はそれにいくつかの工夫を加えました。まず、彼らがやるポインタがヒープに割り当てられ、何かをするだけでなく参照できるように、アドレス演算子を持っていますが、任意の変数に関係なく、それが割り当てられていますかの。第二に、ポインターの算術を定義し、配列の添字を基本的にポインター算術の略記法として定義するため、ポインター算術はほとんどのCおよびC ++で遍在しています(やむを得ないことの次に)。

Javaが発明されたとき、Sunは「Javaにはポインターがない」と、「Javaのほとんどすべてがポインターであるが、これらのポインターはほとんどCの代わりにPascalのようなもの」よりもシンプルでクリーンなマーケティングメッセージだと考えていたようです。彼らは「ポインター」は受け入れられる用語ではないと判断したため、他の何かが必要であり、その参照はAlgol 68の参照と微妙に(そして場合によってはそれほど微妙ではありませんが)代わりに「参照」をdrしました。

多少異なっていましたが、C ++にはほぼ同じ問題がありました。「ポインター」という言葉はすでに知られ、理解されていたので、追加する別のことを指す別の言葉が必要でしたが、人々が意味する「ポインタ」を理解したものとは少し異なります。そのため、Algol 68のリファレンスとは著しく異なりますが、「リファレンス」という用語も再利用しました。


Algol 68で特に痛いのは、自動逆参照です。1レベルに制限されている限り、これは便利なことです。しかし、無意識の目への言及のいくつかを隠す構文糖と組み合わせて、それが数回行われるという事実は、それを混乱させました。
AProgrammer 14年

0

「参照型」の概念は一般的なものであり、「値型」とは対照的に、ポインターと参照の両方を(C ++の観点から)表現できます。Javaの参照は->*逆参照の構文オーバーヘッドのない実際のポインターです。C ++でのJVMの実装を調べると、それらは本当にむき出しのポインターです。また、C#にはJavaに似た参照の概念がありますが、関数パラメーターにポインターと 'ref'修飾子があり、参照によって値型を渡し&、C ++のようにコピーを避けることができます。


この回答は、ここでの以前の回答とどのように違いますか?
ブヨ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.