Pourquoi C ++ et Java utilisent-ils tous deux la notion de «référence» mais pas dans le même sens?

26

En C ++, un argument de référence à une fonction permet à la fonction de faire référence à quelque chose d'autre:

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

De façon similaire, un argument de référence constant à une fonction générera une erreur de temps de compilation si nous essayons de changer la référence:

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

Maintenant, avec Java, les documents disent que les arguments de fonctions de types non primitifs sont des références. Exemple tiré des documents officiels:

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

Ensuite, cercle est assigné une référence à un nouvel objet Circle avec x = y = 0. Cette réaffectation n'a cependant pas de permanence, car la référence a été transmise par valeur et ne peut pas changer.

Pour moi, cela ne ressemble pas du tout aux références C ++. Il ne ressemble pas aux références C ++ normales parce que vous ne pouvez pas le faire référence à autre chose, et il ne ressemble pas aux références const C ++ car en Java, le code qui changerait (mais vraiment pas) la référence ne lance pas de compilation -heure d'erreur.

Ce comportement est plus similaire aux pointeurs C ++. Vous pouvez l'utiliser pour modifier les valeurs des objets pointés, mais vous ne pouvez pas modifier la valeur du pointeur elle-même dans une fonction. En outre, comme avec les pointeurs C ++ (mais pas avec les références C ++), en Java, vous pouvez passer "null" comme valeur pour un tel argument.

Ma question est donc la suivante: pourquoi Java utilise-t-il la notion de «référence»? Faut-il comprendre qu'ils ne ressemblent pas aux références C ++? Ou ressemblent-ils vraiment à des références C ++ et il me manque quelque chose?

Shivan Dragon
la source
10
Notez que dans votre premier exemple C ++, vous ne faites pas la référence "pointer vers autre chose". Au lieu de cela, vous modifiez la valeur de la variable vers laquelle les points de référence sont définis. Ceci est conceptuellement similaire à la mutation de l'état de l'objet auquel il est fait référence avec la référence Java,
Xion
@Xion yep, maintenant que j'ai lu votre commentaire, je vois ce que vous voulez dire et je suis d'accord avec 98% de ce que vous dites :) Le problème en Java est que vous devez avoir accès à tous les états de l'objet afin de réaliser ce que C ++ fait simplement en vous permettant de définir la référence à la valeur d'une autre référence.
Shivan Dragon

Réponses:

48

Pourquoi? Parce que, même si une terminologie cohérente est généralement bonne pour l'ensemble de la profession, les concepteurs de langues ne respectent pas toujours l'utilisation des langues d'autres concepteurs de langues, en particulier si ces autres langues sont perçues comme des concurrents.

Mais vraiment, ni l'utilisation de la «référence» n'était un très bon choix. Les "références" en C ++ sont simplement une construction de langage pour introduire explicitement des alias (noms alternatifs pour exactement la même entité). Les choses auraient été beaucoup plus claires car ils avaient simplement appelé la nouvelle fonctionnalité "alias" en premier lieu. Cependant, à cette époque, la grande difficulté était de faire comprendre à tout le monde la différence entre les pointeurs (qui nécessitent un déréférencement) et les références (qui ne le font pas), donc l'important était qu'on l'appelait autre chose que "pointeur", et non pas plus précisément quel terme utiliser.

Java n'a pas de pointeurs et en est fier, donc utiliser "pointeur" comme terme n'était pas une option. Cependant, les "références" qu'il a se comportent un peu comme les pointeurs de C ++ lorsque vous les passez - la grande différence est que vous ne pouvez pas faire les opérations de bas niveau les plus méchantes (casting, ajout ...) sur eux , mais ils donnent exactement la même sémantique lorsque vous passez des poignées à des entités identiques par rapport à des entités qui se trouvent être égales. Malheureusement, le terme "pointeur" comporte tellement d'associations négatives de bas niveau qu'il est peu probable qu'il soit accepté par la communauté Java.

Le résultat est que les deux langues utilisent le même terme vague pour deux choses assez différentes, qui pourraient toutes deux bénéficier d'un nom plus spécifique, mais aucune des deux ne devrait être remplacée prochainement. Le langage naturel peut aussi parfois être frustrant!

Kilian Foth
la source
7
Merci, penser les références C ++ comme des "alias" (pour la même valeur / instance) et les références Java comme des "pointeurs sans les opérations de bas niveau les plus méchantes (casting, ajout ...)" clarifie vraiment les choses pour moi.
Shivan Dragon du
14
Java n'a pas de pointeurs et en est fier, donc utiliser "pointeur" comme terme n'était pas une option. Qu'en est-il NullPointerExceptiondu fait que le JLS utilise le terme "pointeur"?
Tobias Brandt du
7
@TobiasBrandt: NullPointerExceptionn'est pas un pointeur, mais c'est une exception indiquant qu'à un niveau inférieur, un pointeur NULL a été transmis / déréférencé. L'utilisation Pointerdans le cadre d'une exception, le cas échéant, ajoute à l'image désagréable qu'ils ont.Le JLS doit mentionner des pointeurs, car la machine virtuelle de Java a été principalement écrite dans un langage qui les utilise. Vous ne pouvez pas décrire avec précision les spécifications linguistiques sans décrire le fonctionnement des
composants
3
@TobiasBrandt: Java utilise des pointeurs partout; les objets de tas sont référencés par quelque chose qui, à toutes fins pratiques, est un pointeur. C'est juste que Java n'expose aucune opération sur les types de pointeurs au programmeur.
John Bode
2
Je préfère le terme "ID d'objet" pour les références Java / .NET. Au niveau du bit, de telles choses peuvent généralement être implémentées comme quelque chose ressemblant à un pointeur, mais rien ne les oblige à l'être. Ce qui est important, c'est qu'un ID d'objet contienne le type d'informations dont le framework / vm a besoin pour identifier un objet.
supercat
9

Une référence est une chose qui fait référence à une autre chose. Diverses langues ont attribué un sens plus spécifique au mot «référence», généralement à «quelque chose comme un pointeur sans tous les mauvais aspects». C ++ attribue une signification spécifique, tout comme Java ou Perl.

En C ++, les références ressemblent plus à des alias (qui peuvent être implémentés via un pointeur). Cela permet de passer par référence ou de sortir des arguments .

En Java, les références sont des pointeurs sauf que ce n'est pas un concept réifié du langage: tous les objets sont des références, les types primitifs comme les nombres ne le sont pas. Ils ne veulent pas dire «pointeur» car il n'y a pas d'arithmétique de pointeur et il n'y a pas de pointeurs réifiés en Java, mais ils veulent préciser que lorsque vous passez un Objectcomme argument, l'objet n'est pas copié . Ce n'est pas non plus un passage par référence , mais quelque chose de plus comme un passage par partage .

amon
la source
6

Cela remonte en grande partie à Algol 68, et en partie à une réaction contre la façon dont C définit les pointeurs.

Algol 68 a défini un concept appelé référence. C'était à peu près la même chose que (pour un exemple) un pointeur en Pascal. Il s'agissait d'une cellule qui contenait NIL ou l'adresse d'une autre cellule d'un type spécifié. Vous pouvez affecter à une référence, afin qu'une référence puisse faire référence à une cellule à la fois et à une cellule différente après avoir été réaffectée. Il n'a pas , cependant, quoi que ce soit un soutien analogue à C ou arithmétique de pointeur C de.

Au moins comme Algol 68 définissait les choses, cependant, les références étaient un concept assez propre qui était assez maladroit à utiliser dans la pratique. La plupart des définitions de variables étaient en fait des références, ils ont donc défini une notation abrégée pour l'empêcher de devenir complètement incontrôlable, mais une utilisation plus banale pourrait devenir maladroite assez rapidement de toute façon.

Par exemple, une déclaration comme a INT j := 7;été vraiment traitée par le compilateur comme une déclaration comme REF INT j = NEW LOC INT := 7. Donc, ce que vous avez déclaré dans Algol 68 était normalement une référence, qui était ensuite initialisée pour faire référence à quelque chose qui était alloué sur le tas, et qui était (facultativement) initialisé pour contenir une valeur spécifiée. Contrairement à Java, cependant, ils ont au moins essayé de vous laisser conserver une syntaxe saine pour cela au lieu d'avoir constamment des choses comme foo bar = new foo();ou d'essayer de dire aux fibs que "nos pointeurs ne sont pas des pointeurs".

Pascal et la plupart de ses descendants (directs et ... spirituels) ont renommé le concept de référence Algol 68 en "pointeur" mais ont gardé le concept lui-même essentiellement le même: un pointeur était une variable qui contenait l'un nilou l' autre , ou l'adresse de quelque chose que vous allouiez sur le tas (c'est-à-dire, au moins tel que défini à l'origine par Jensen et Wirth, il n'y avait pas d'opérateur "adresse de", donc il n'y avait aucun moyen pour un pointeur de se référer à une variable normalement définie). Bien qu'il s'agisse de «pointeurs», aucune arithmétique des pointeurs n'était prise en charge.

C et C ++ ont ajouté quelques changements à cela. Tout d' abord, ils n'ont une adresse de l' opérateur, de sorte qu'un pointeur peut se référer non seulement à quelque chose alloué sur le tas, mais à une variable, quelle que soit la façon dont il est affecté. Deuxièmement, ils définissent l'arithmétique sur les pointeurs - et définissent les indices de tableau comme simplement une notation abrégée pour l'arithmétique des pointeurs, de sorte que l'arithmétique des pointeurs est omniprésente (à côté d'inévitable) dans la plupart des C et C ++.

Lorsque Java a été inventé, Sun a apparemment pensé que "Java n'a pas de pointeurs" était un message marketing plus simple et plus propre que: "presque tout en Java est un pointeur, mais ces pointeurs sont principalement comme Pascal au lieu de C." Puisqu'ils avaient décidé que "pointeur" n'était pas un terme acceptable, ils avaient besoin d'autre chose, et ont plutôt dragué "référence", même si leurs références étaient subtilement (et dans certains cas pas si subtilement) différentes des références Algol 68.

Bien qu'il soit sorti quelque peu différemment, C ++ était coincé avec à peu près le même problème: le mot "pointeur" était déjà connu et compris, donc ils avaient besoin d'un mot différent pour cette chose différente qu'ils ajoutaient qui faisait référence à autre chose, mais était par ailleurs tout à fait un peu différent de ce que les gens entendaient par "pointeur". Ainsi, même si elle est également sensiblement différente d'une référence Algol 68, ils ont également réutilisé le terme "référence".

Jerry Coffin
la source
Ce qui rend la référence dans Algol 68 particulièrement douloureuse, c'est le déréférencement automatique. C'est quelque chose de pratique tant qu'il est limité à un niveau. Mais le fait que cela puisse être fait plusieurs fois combiné avec le sucre syntaxique qui masquait certaines des références aux yeux inconscients le rendait confus.
AProgrammer
0

La notion de «type de référence» est générique, elle peut exprimer à la fois un pointeur et une référence (en termes de C ++), par opposition au «type de valeur». Les références Java sont vraiment des pointeurs sans surcharge syntaxique d' ->un *déréférencement. Si vous regardez l'implémentation de JVM en C ++, ce sont vraiment des pointeurs nus. De plus, C # a une notion de références similaires à celles de Java, mais il a également des pointeurs et des qualificateurs 'ref' sur les paramètres de fonction, permettant de passer des types de valeurs par référence et d'éviter la copie comme &en C ++.

Hertz
la source
en quoi cette réponse est-elle différente / meilleure des réponses précédentes ici?
moucher