Perché C ++ e Java usano entrambi la nozione di "riferimento" ma non nello stesso senso?

24

In C ++ un argomento di riferimento a una funzione consente alla funzione di fare riferimento a qualcos'altro:

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;
}

Analogamente, un argomento di riferimento costante a una funzione genera un errore in fase di compilazione se proviamo a cambiare il riferimento:

void changeNumberReference(const int& reference) {
    reference = replacement; // compile-time error: assignment of read-only reference 'reference'
}

Ora, con Java, i documenti dicono che gli argomenti delle funzioni di tipi non primitivi sono riferimenti. Esempio dai documenti ufficiali:

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);
}

Then circle is assigned a reference to a new Circle object with x = y = 0. This reassignment has no permanence, however, because the reference was passed in by value and cannot change.

Per me questo non assomiglia affatto ai riferimenti C ++. Non assomiglia ai normali riferimenti C ++ perché non è possibile fare riferimento a qualcos'altro, e non assomiglia ai riferimenti const C ++ perché in Java, il codice che cambierebbe (ma in realtà non lo fa) il riferimento non genera una compilazione -time error.

Questo è più simile nel comportamento ai puntatori C ++. Puoi usarlo per modificare i valori degli oggetti appuntiti, ma non puoi modificare il valore del puntatore stesso in una funzione. Inoltre, come con i puntatori C ++ (ma non con i riferimenti C ++), in Java puoi passare "null" come valore per tale argomento.

Quindi la mia domanda è: perché Java usa la nozione di "riferimento"? Si deve capire che non assomigliano ai riferimenti C ++? O davvero assomigliano davvero ai riferimenti C ++ e mi manca qualcosa?

    
posta Shivan Dragon 08.01.2014 - 12:01
fonte

4 risposte

47

Perché? Perché, sebbene la terminologia coerente sia generalmente valida per l'intera professione, i progettisti di linguaggi non sempre rispettano l'uso linguistico di altri progettisti di linguaggi, in particolare se tali altre lingue sono percepite come concorrenti.

Ma in realtà, né l'uso di "riferimento" è stata una scelta molto buona. I "riferimenti" in C ++ sono semplicemente un costrutto di linguaggio per introdurre esplicitamente alias (nomi alternativi per esattamente la stessa entità). Le cose sarebbero state molto più chiare di aver semplicemente chiamato la nuova funzione "alias" in primo luogo. Tuttavia, in quel momento la grande difficoltà era far capire a tutti la differenza tra i puntatori (che richiedono il dereferenziamento) ei riferimenti (che non lo fanno), quindi l'importante era che fosse chiamato qualcosa di diverso da "puntatore", e non così molto specificamente quale termine usare.

Java non ha puntatori e ne è orgoglioso, quindi usare "puntatore" come termine non era un'opzione. Tuttavia, i "riferimenti" che ha si comportano un po 'come fanno i puntatori di C ++ quando li si passa in giro - la grande differenza è che non si possono fare le peggiori operazioni a basso livello (lanciare, aggiungere ...) su di loro , ma risultano esattamente nella stessa semantica quando si passano le maniglie a entità identiche a quelle che sono semplicemente uguali. Sfortunatamente, il termine "puntatore" contiene così tante associazioni negative di basso livello che è improbabile che venga accettata dalla comunità Java.

Il risultato è che entrambe le lingue usano lo stesso termine vago per due cose piuttosto diverse, entrambe le quali potrebbero trarre profitto da un nome più specifico, ma nessuna delle due sarà probabilmente sostituita in qualsiasi momento. Il linguaggio naturale, a volte, può essere frustrante!

    
risposta data 08.01.2014 - 12:15
fonte
9

Un riferimento è una cosa che si riferisce a un'altra cosa. Diverse lingue hanno attribuito un significato più specifico alla parola "riferimento", di solito a "qualcosa come un puntatore senza tutti gli aspetti negativi". C ++ attribuisce un significato specifico, come Java o Perl.

In C ++, i riferimenti sono più simili agli alias (che possono essere implementati tramite un puntatore). Ciò consente di passare per riferimento o fuori argomenti .

In Java, i riferimenti sono puntatori ad eccezione del fatto che questo non è un concetto reificato del linguaggio: tutti gli oggetti sono riferimenti, i tipi primitivi come i numeri non lo sono. Non vogliono dire "puntatore" perché non c'è aritmetica puntatore e non ci sono puntatori reificati in Java, ma vogliono chiarire che quando si passa un Object come argomento, l'oggetto è non copiato . Anche questo non è passare per riferimento , ma qualcosa di simile a passare condividendo .

    
risposta data 08.01.2014 - 12:15
fonte
6

Risale in gran parte ad Algol 68 e in parte a una reazione contro il modo in cui C definisce i puntatori.

Algol 68 ha definito un concetto chiamato riferimento. Era praticamente lo stesso di (per un esempio) un puntatore in Pascal. Era una cella che conteneva o NIL o l'indirizzo di qualche altra cella di un certo tipo specificato. È possibile assegnare a un riferimento, in modo che un riferimento possa fare riferimento a una cella alla volta e una cella diversa dopo essere stata riassegnata. Ha fatto non , tuttavia, supporta qualcosa di analogo all'aritmetica dei puntatori C o C ++.

Almeno come Algol 68 ha definito le cose, tuttavia, i riferimenti erano un concetto abbastanza pulito che era abbastanza goffo da usare nella pratica. La maggior parte delle definizioni di variabili erano in realtà di riferimenti, quindi hanno definito una notazione abbreviata per evitare che diventasse completamente fuori controllo, ma qualcosa di più di un uso banale potrebbe diventare abbastanza impacciato in ogni caso.

Ad esempio, una dichiarazione come INT j := 7; è stata veramente trattata dal compilatore come una dichiarazione come REF INT j = NEW LOC INT := 7 . Quindi, quello che hai dichiarato in Algol 68 era normalmente un riferimento, che è stato quindi inizializzato per fare riferimento a qualcosa che è stato allocato sull'heap e che è stato (facoltativamente) inizializzato per contenere un valore specificato. A differenza di Java, tuttavia, hanno almeno tentato di mantenere una sintassi corretta per quello invece di avere costantemente cose come foo bar = new foo(); o provare a dire a fibs "i nostri indicatori non sono puntatori."

Pascal e la maggior parte dei suoi discendenti (sia diretti che ... spirituali) rinominarono il concetto di riferimento di Algol 68 in "puntatore" ma mantennero il concetto stesso essenzialmente lo stesso: un puntatore era una variabile che conteneva nil , o l'indirizzo di qualcosa che hai allocato sullo heap (cioè, almeno come originariamente definito da Jensen e Wirth, non c'era un operatore "address-of", quindi non c'era modo per un puntatore di riferirsi a una variabile normalmente definita). Nonostante siano "puntatori", non è stata supportata l'aritmetica dei puntatori.

C e C ++ hanno aggiunto alcuni colpi di scena. Innanzitutto, fanno hanno un operatore address-of, quindi un puntatore può riferirsi non solo a qualcosa che è stato assegnato all'heap, ma a qualsiasi variabile, indipendentemente da come è allocata. In secondo luogo, definiscono l'aritmetica sui puntatori e definiscono gli indici di matrice come fondamentalmente solo una notazione abbreviata per l'aritmetica del puntatore, quindi l'aritmetica dei puntatori è ubiquitaria (quasi inevitabile) nella maggior parte dei C e C ++.

Quando Java fu inventato, a quanto pare Sun pensava che "Java non avesse puntatori" era un messaggio di marketing più semplice e più pulito di: "quasi tutto in Java è un puntatore, ma questi puntatori sono principalmente come quelli di Pascal anziché di C." Dal momento che avevano deciso che "pointer" non era un termine accettabile, avevano bisogno di qualcos'altro, e invece dragavano il "riferimento", anche se i loro riferimenti erano sottilmente (e in alcuni casi non così sottilmente) diversi dai riferimenti di Algol 68. / p>

Sebbene venisse fuori in un modo un po 'diverso, il C ++ è rimasto più o meno lo stesso problema: la parola "puntatore" era già nota e comprensibile, quindi avevano bisogno di una parola diversa per questa cosa diversa che aggiungevano che si riferiva a qualcos'altro, ma era altrimenti abbastanza diverso da quello che la gente intendeva dire "puntatore". Quindi, anche se è notevolmente diverso da un riferimento ad Algol 68, hanno riutilizzato anche il termine "riferimento".

    
risposta data 20.01.2014 - 11:04
fonte
0

La nozione di "tipo di riferimento" è generica, può esprimere sia un puntatore che un riferimento (in termini di C ++), anziché "tipo di valore". I riferimenti di Java sono in realtà puntatori senza overhead sintattico di -> an * dereferencing. Se guardi all'implementazione di JVM in C ++, sono davvero dei semplici suggerimenti. Inoltre, C # ha una nozione di riferimenti simili a quelli di Java, ma ha anche puntatori e qualificatori 'ref' sui parametri di funzione, consentendo di passare tipi di valori per riferimento ed evitare di copiare come & in C ++.

    
risposta data 09.01.2014 - 09:44
fonte

Leggi altre domande sui tag