Почему 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);
}

Тогда окружности присваивается ссылка на новый объект Circle с x = y = 0. Однако это переназначение не имеет постоянства, поскольку ссылка была передана по значению и не может быть изменена.

Для меня это совсем не похоже на ссылки на C ++. Он не похож на обычные ссылки C ++, потому что вы не можете заставить его ссылаться на что-то другое, и он не похож на константные ссылки C ++, потому что в Java код, который изменит (но на самом деле не делает) ссылку, не вызывает компиляцию ошибка

По поведению это больше похоже на указатели C ++. Вы можете использовать его для изменения значений указанных объектов, но вы не можете изменить само значение указателя в функции. Кроме того, как и в случае с указателями C ++ (но не со ссылками на C ++), в Java вы можете передать «null» в качестве значения для такого аргумента.

Итак, мой вопрос: почему Java использует понятие «ссылка»? Следует ли понимать, что они не похожи на ссылки C ++? Или они действительно напоминают ссылки на C ++, а я что-то упустил?


10
Обратите внимание, что в вашем первом примере C ++ вы не делаете ссылку "указать на что-то еще". Вместо этого вы меняете значение переменной, на которую указывает ссылка. Концептуально это похоже на изменение состояния объекта, на который ссылается Java-ссылка,
Xion

@Xion Да, теперь, когда я прочитал ваш комментарий, я понимаю, что вы имеете в виду, и согласен с 98% того, что вы говорите :) Проблема в Java заключается в том, что вы должны иметь доступ ко всем состояниям объекта, чтобы концептуально добиться того, что просто делает C ++, позволяя установить ссылку на значение какой-либо другой ссылки.
Shivan Dragon

Ответы:


48

Зачем? Поскольку, хотя согласованная терминология, как правило, полезна для всей профессии, разработчики языка не всегда уважают использование языка другими разработчиками языка, особенно если эти другие языки воспринимаются как конкуренты.

Но на самом деле ни использование «ссылки» не было очень хорошим выбором. «Ссылки» в C ++ - это просто языковая конструкция, чтобы представить явного псевдонимов (альтернативных имен для одной и той же сущности). Все было бы намного яснее, если бы они просто называли новую функцию «псевдонимами». Однако в то время большая трудность состояла в том, чтобы заставить всех понять разницу между указателями (которые требуют разыменования) и ссылками (которые этого не делают), поэтому важно было то, что он назывался не «указателем», а не так. более конкретно, какой термин использовать.

Java не имеет указателей и гордится этим, поэтому использование «указателя» в качестве термина не было вариантом. Тем не менее, «ссылки», которые он выполняет, ведут себя немного так же, как указатели в C ++, когда вы их передаете, - большая разница в том, что вы не можете выполнять над ними более низкоуровневые операции (приведение, добавление ...) , но они приводят к точно такой же семантике, когда вы передаете дескрипторы идентичным сущностям по сравнению с сущностями, которые просто оказываются равными. К сожалению, термин «указатель» несет в себе так много негативных низкоуровневых ассоциаций, что это вряд ли когда-либо будет принято сообществом Java.

В результате оба языка используют один и тот же расплывчатый термин для двух довольно разных вещей, оба из которых могут получить выгоду от более конкретного имени, но ни один из которых, вероятно, не будет заменен в ближайшее время. Естественный язык тоже может иногда расстраивать!


7
Спасибо, мысль о ссылках на C ++ как о «псевдонимах» (для того же значения / экземпляра) и о ссылках на Java как о «указателях без мерзких низкоуровневых операций (приведение, добавление ...)» действительно проясняет для меня вещи.
Shivan Dragon

14
Java не имеет указателей и гордится этим, поэтому использование «указателя» в качестве термина не было вариантом. Как насчет NullPointerExceptionтого факта, что JLS использует термин «указатель»?
Тобиас Брандт

7
@TobiasBrandt: NullPointerExceptionне указатель, но исключение, указывающее, что на более низком уровне указатель NULL был передан / разыменован. Использование Pointerв качестве исключения, если что-либо, добавляет к неприятному образу, который у них есть. JLS должен упоминать указатели, потому что виртуальная машина Java была написана в основном на языке, который их использует. Вы не можете точно описать языковые спецификации, не описав, как работают внутренние устройства
Элиас Ван Оотегем

3
@TobiasBrandt: Java использует указатели повсюду; К объектам кучи обращаются через что-то, что для всех практических целей является указателем. Просто Java не предоставляет программисту никаких операций над типами указателей.
Джон Боде

2
Я предпочитаю термин «идентификатор объекта» для ссылок Java / .NET. На уровне битов такие вещи обычно могут быть реализованы как нечто, напоминающее указатель, но ничто не требует от них этого. Важно то, что идентификатор объекта содержит любую информацию, необходимую фреймворку / vm для идентификации объекта.
суперкат

9

Ссылка - это вещь, которая относится к другой вещи. Различные языки придают более конкретное значение слову «ссылка», обычно «что-то вроде указателя без всех плохих аспектов». C ++ придает особое значение, как и Java или Perl.

В C ++ ссылки больше похожи на псевдонимы (которые могут быть реализованы с помощью указателя). Это позволяет передавать по ссылке или из аргументов .

В Java ссылки являются указателями, за исключением того, что это не конкретизированная концепция языка: все объекты являются ссылками, а примитивные типы, такие как числа, - нет. Они не хотят произносить «указатель», потому что в Java нет арифметики указателей и нет указателей reified, но они хотят прояснить, что при передаче Objectв качестве аргумента объект не копируется . Это также не передача по ссылке , а что-то более похожее на передачу .


6

Это в основном восходит к Алголу 68, и частично к реакции на то, как С определяет указатели.

Алгол 68 определил понятие, называемое ссылкой. Это было почти так же, как (для одного примера) указатель в Паскале. Это была ячейка, которая содержала либо NIL, либо адрес какой-то другой ячейки определенного типа. Вы можете назначить ссылку, чтобы ссылка могла ссылаться на одну ячейку за раз и на другую ячейку после переназначения. Однако он не поддерживает ничего похожего на арифметику указателей в C или C ++.

Однако, по крайней мере, как определил Алгол 68, ссылки были довольно чистым понятием, которое было довольно неуклюжим для использования на практике. Большинство определений переменных были на самом деле ссылками, поэтому они определили сокращенную запись, чтобы не дать ей полностью выйти из-под контроля, но любое более простое использование в любом случае могло довольно быстро стать неуклюжим.

Например, объявление like INT j := 7;было действительно обработано компилятором как объявление like REF INT j = NEW LOC INT := 7. Итак, то, что вы объявили в Algol 68, обычно было ссылкой, которая затем инициализировалась для ссылки на что-то, что было выделено в куче, и это (необязательно) было инициализировано, чтобы содержать некоторое указанное значение. Однако, в отличие от Java, они, по крайней мере, пытались позволить вам поддерживать нормальный синтаксис для этого, вместо того, чтобы постоянно иметь подобные вещи foo bar = new foo();или пытаться рассказывать выдумкам о том, что «наши указатели не являются указателями».

Паскаль и большинство его потомков (как прямых, так и ... духовных) переименовали эталонную концепцию Algol 68 в «указатель», но оставили саму концепцию по существу такой же: указатель был переменной, которая содержала либо nilадрес чего-то, что вы выделили в куче (т. е. по крайней мере, как изначально определено Дженсеном и Виртом, не было оператора «address-of», поэтому у указателя не было возможности ссылаться на нормально определенную переменную). Несмотря на то, что они были «указателями», арифметика указателей не поддерживалась.

C и C ++ добавили несколько поворотов к этому. Во- первых, они делают имеют адрес-оператора, поэтому указатель может относиться не только к чему - то выделенной в куче, но и к любой переменной, независимо от того, как он выделяется. Во-вторых, они определяют арифметику для указателей - и определяют индексы массива как в основном просто сокращенную запись для арифметики указателей, поэтому арифметика указателей повсеместна (рядом с неизбежной) в большинстве C и C ++.

Когда изобрели Java, Sun, по-видимому, думал, что «Java не имеет указателей» было более простым и понятным маркетинговым сообщением, чем: «почти все в Java является указателем, но эти указатели в основном похожи на Паскаль, а не на С». Поскольку они решили, что «указатель» не является приемлемым термином, им нужно было что-то еще, и вместо этого они взяли «ссылку», хотя их ссылки слегка (а в некоторых случаях не так тонко) отличались от ссылок на Алгол 68.

Хотя все вышло несколько иначе, в C ++ возникла примерно та же проблема: слово «указатель» уже было известно и понято, поэтому им нужно было другое слово для этой другой вещи, которую они добавляли, которая ссылалась на что-то другое, но в остальном это было довольно немного отличается от того, что люди понимают под «указателем». Таким образом, хотя он также заметно отличается от ссылки на Algol 68, они также повторно использовали термин «ссылка».


Что делает ссылку в Алголе 68 особенно болезненной, так это автоматическое разыменование. Это что-то удобное, если оно ограничено одним уровнем. Но тот факт, что это можно было сделать несколько раз в сочетании с синтаксическим сахаром, который скрывал некоторые ссылки на неосведомленные глаза, приводил в замешательство.
AProgrammer

0

Понятие «ссылочный тип» является общим, оно может выражать как указатель, так и ссылку (в терминах C ++), а не «тип значения». Ссылки Java являются действительно указатели без синтаксического погона ->с *разыменования. Если вы посмотрите на реализацию JVM в C ++, они действительно голые указатели. Кроме того, в C # есть понятие ссылок, аналогичное Java, но также есть указатели и квалификаторы 'ref' на параметры функции, позволяющие передавать типы значений по ссылке и избегать копирования, как &в C ++.


чем этот ответ отличается / лучше от предыдущих ответов здесь?
комнат
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.