Чому 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 ++ посилання const, оскільки в Java код, який міняв би (але насправді не робить) посилання, не кидає компіляцію -помилка часу.

Це більш схоже за поведінкою на покажчики C ++. Ви можете використовувати його для зміни значень загострених об'єктів, але ви не можете змінити значення функції вказівника у функції. Також, як і вказівники на C ++ (але не з посиланнями на C ++), у Java ви можете передавати "null" як значення для такого аргументу.

Отже, моє запитання: Чому Java використовує поняття "посилання"? Чи варто розуміти, що вони не нагадують посилання на C ++? Або вони справді нагадують посилання на C ++, і я чогось пропускаю?


10
Зауважте, що у своєму першому прикладі C ++ ви не робите посилання "вказувати на щось інше". Натомість ви змінюєте значення змінної, на яку посилаються опорні точки. Це концептуально схоже на мутацію стану об'єкта, на який посилається посилання на Java,
Xion

@Xion так, тепер, коли я прочитав ваш коментар, я бачу, що ви маєте на увазі і погоджуєтесь з 98% того, що ви говорите :) Проблема в Java полягає в тому, що ви повинні мати доступ до будь-якого стану об'єкта, щоб концептуально досягти того, що C ++ просто робить, дозволяючи вам встановити посилання на значення якоїсь іншої посилання.
Дракон Шиван

Відповіді:


48

Чому? Оскільки, хоча послідовна термінологія, як правило, корисна для всієї професії, мовні дизайнери не завжди поважають використання мови інших дизайнерів мови, особливо якщо ці інші мови сприймаються як конкуренти.

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

У Java немає вказівників, і цим пишається, тому використання "покажчика" як терміна не було варіантом. Однак, "посилання", які він має, поводяться зовсім небагато, як це роблять покажчики C ++, коли ви передаєте їх навколо - велика різниця в тому, що ви не можете робити гидкі операції низького рівня (кастинг, додавання ...) на них , але вони призводять до точно такої ж семантики, коли ви передаєте ручки сутностям, які однакові проти сутностей, які просто стають рівними. На жаль, термін "покажчик" містить стільки негативних асоціацій низького рівня, що навряд чи колись його прийме спільнота Java.

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


7
Дякую, мислення посилань на C ++ як "псевдонімів" (для одного і того ж значення / екземпляра) та посилань на Java як "покажчиків без найгірших операцій низького рівня (кастинг, додавання ...)" дійсно прояснює для мене речі.
Дракон Шиван

14
У Java немає вказівників, і цим пишається, тому використання "покажчика" як терміна не було варіантом. А як щодо NullPointerExceptionтого, що JLS використовує термін "покажчик"?
Тобіас Брандт

7
@TobiasBrandt: NullPointerExceptionне вказівник, але це виняток, що вказує на те, що на нижньому рівні вказівник NULL переданий / відпущений. Використання в Pointerякості частині виключення, якщо що - небудь, додає до неприємних зображень вони have.The JLS має згадати покажчики, тому що VM Java була в основному написана на мові , який використовує їх. Ви не можете точно описати мовні характеристики, не описуючи, як працюють внутрішні організації
Elias Van Ootegem

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

2
Я віддаю перевагу терміну "ідентифікатор об'єкта" для посилань на Java / .NET На рівні бітів такі речі зазвичай можуть бути реалізовані як щось, що нагадує вказівник, але нічого цього не вимагає. Важливо те, що ідентифікатор об'єкта містить будь-яку інформацію, яка потрібна рамці / vm для ідентифікації об'єкта.
supercat

9

Посилання - це річ, яка посилається на іншу річ. Різні мови приписують слову "посилання" більш специфічне значення, як правило, "щось таке, як вказівник без усіх поганих аспектів". C ++ приписує конкретне значення, як і Java або Perl.

У C ++ посилання більше нагадують псевдоніми (які можна реалізувати за допомогою вказівника). Це дозволяє передавати аргументи шляхом посилання чи виходу .

У Java посилання є вказівниками, за винятком того, що це не вдосконалена концепція мови: Усі об'єкти є посиланнями, примітивні типи, такі як числа, не є. Вони не хочуть говорити "вказівник", оскільки не існує арифметики вказівника, а в Java немає перероблених покажчиків, але вони хочуть зрозуміти, що при передачі Objectаргументу об'єктом є не копіюється . Це також не є посиланням , але щось більше, як передача шляхом спільного використання .


6

Це значною мірою сходить до Algol 68, а частково до реакції проти того, як C визначає покажчики.

Алгол 68 визначив поняття, яке називалося еталонним. Це було майже те саме, що (на один приклад) вказівник у Паскалі. Це була комірка, яка містила або NIL, або адресу якоїсь іншої комірки певного типу. Ви можете призначити посилання, щоб посилання могло посилатися на одну клітинку одночасно, а іншу комірку після переназначення. Це булоОднак не підтримував нічого аналогічного арифметиці вказівника C або C ++.

Принаймні, як визначали речі Algol 68, проте, посилання були досить чистою концепцією, яка була досить незграбною для використання на практиці. Більшість змінних визначень насправді були посиланнями, тому вони визначали скорочену позначення, щоб не повністю вийти з ладу, але будь-яке, крім тривіального використання, все одно може отримати незграбність досить швидко.

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

Паскаль та більшість його нащадків (як прямих, так і ... духовних) перейменували опорну концепцію Алгола 68 на "вказівник", але сама концепція зберегла по суті те саме: вказівник був змінною, яка містила або nil або адресу, або адресу чогось, що ви виділили на купі (тобто, принаймні так, як спочатку вони визначали Дженсен і Вірт, не було оператора "адреса", тому вказівник не міг звертатися до нормально визначеної змінної). Незважаючи на те, що вони були "покажчиками", жодна арифметика вказівника не підтримувалася.

C і C ++ додали до цього кілька поворотів. По- перше, вони роблять мають адрес-оператора, тому покажчик може ставитися не тільки до чого - то виділеної в купі, але і до будь-якої змінної, незалежно від того, як він виділяється. По-друге, вони визначають арифметику на покажчиках - і визначають підписники масиву як в основному лише скорочення позначень арифметики вказівника, тому арифметика вказівника є всюдисущою (поруч із неминучим) у більшості C та C ++.

Коли Java вигадувалась, Соня, мабуть, думав, що "Java не має покажчиків" було більш простим, чистішим маркетинговим повідомленням, ніж: "майже все в Java - це вказівник, але ці вказівники здебільшого схожі на Паскаля замість C". Оскільки вони вирішили, що "покажчик" не є прийнятним терміном, їм було потрібно щось інше, і замість цього заглушили "посилання", хоча їх посилання були тонко (а в деяких випадках і не настільки тонкі) відрізняються від посилань на Алгол 68.

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


Що робить посилання в Algol 68 особливо болючим, це автоматичне перенаправлення. Це щось зручне, поки воно обмежене одним рівнем. Але той факт, що це можна зробити кілька разів у поєднанні з синтаксичним цукром, який приховував деякі згадки про невідомі очі, робив це заплутаним.
AProgrammer

0

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


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