为什么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的新Circle对象的引用。但是,这种重新分配没有永久性,因为该引用是按值传递的并且不能更改。

对我来说,这看起来并不像C ++引用。它不像常规的C ++引用,因为您不能使它引用其他东西,也不像C ++的const引用,因为在Java中,可能会更改(但实际上不会)的代码不会引发编译时间错误。

这在行为上与C ++指针更相似。您可以使用它来更改指针对象的值,但是不能在函数中更改指针的值本身。同样,与C ++指针(但不包含C ++引用)一样,在Java中,您可以将“ null”作为此类参数的值。

所以我的问题是:为什么Java使用“引用”概念?是否可以理解它们与C ++引用不同?还是它们确实确实类似于C ++引用,但我缺少某些东西?


10
请注意,在您的第一个C ++示例中,您没有引用“指向其他内容”。相反,您正在更改参考指向的变量的值。从概念上讲,这类似于更改Java参考所引用的对象的状态
Xion 2014年

@Xion是的,现在我已经阅读了您的评论,我明白了您的意思,并同意您所说的98%的意思:) Java中的问题是,必须从概念上访问对象的所有状态通过将引用设置为其他引用的值来实现C ++的功能。
Shivan Dragon

Answers:


48

为什么?因为尽管一致的术语通常对整个行业都有利,但是语言设计师并不总是尊重其他语言设计师的语言使用,尤其是在那些其他语言被视为竞争对手的情况下。

但是实际上,都不使用“引用”是一个很好的选择。C ++中的“参考”只是一种语言构造,用于明确引入别名(完全相同的实体的替代名称)。事情将更加清晰,因为它们最初只是简单地将新功能称为“别名”。但是,当时最大的困难是使每个人都了解指针(需要取消引用)和引用(不需要引用)之间的区别,所以重要的是它被称为“指针”之外的东西,而并非如此。特别是要使用什么术语。

Java没有指针,并且为此感到自豪,因此使用“指针”作为术语是没有选择的。但是,它确实具有与C ++指针一样的“引用”,当您传递它们时-很大的区别是您无法对它们执行更原始的低级操作(广播,添加...) ,但是当您将句柄传递给相同实体与恰好相等的实体时,它们会产生完全相同的语义。不幸的是,“指针”一词包含了许多负面的低级关联,因此Java社区不太可能接受它。

结果是两种语言对两个相当不同的事物使用相同的模糊术语,这两种语言都可能受益于更特定的名称,但是两种语言都不会很快被替换。自然语言有时也会令人沮丧!


7
谢谢,将C ++引用视为“别名”(具有相同的值/实例),而将Java引用视为“没有更原始的低级操作的指针(广播,添加...)”对我来说确实很清楚。
Shivan Dragon 2014年

14
Java没有指针,并且为此感到自豪,因此使用“指针”作为术语是没有选择的。什么NullPointerException或者说,JLS使用术语“指针”的事实呢?
Tobias Brandt 2014年

7
@TobiasBrandt:NullPointerException不是指针,但是它是一个异常,它指示在较低级别上传递/取消引用了NULL指针。使用它Pointer作为异常的一部分(如果有的话)会增加它们的讨厌形象。JLS 必须提及指针,因为Java的VM主要是使用使用它们的语言编写的。您无法准确地描述语言规范,而不描述内部结构如何工作
Elias Van Ootegem 2014年

3
@TobiasBrandt:Java在各处使用指针;堆对象通过某种东西(实际上所有目的都是指针)进行引用。只是Java没有向程序员公开对指针类型的任何操作。
约翰·博德2014年

2
对于Java / .NET参考,我更喜欢术语“对象ID”。在位级别上,此类事物通常可以实现为类似于指针的事物,但没有任何要求。重要的是对象ID包含框架/ vm识别对象所需的任何信息。
2014年

9

引用是指另一个事物的事物。各种语言都给“引用”一词赋予了更具体的含义,通常是“诸如没有所有不良方面的指针之类的东西”。C ++赋予了特定的含义,Java或Perl也是如此。

在C ++中,引用更像是别名(可以通过指针来实现)。这允许按引用传递传递参数

在Java中,引用是指针,除了这不是语言的概念化:所有对象都是引用,而不是数字等原始类型。他们不想说“指针”,因为在Java中没有指针算术并且没有经过修饰的指针,但是他们想弄清楚当您将an Object作为参数传递时,不会复制对象。这也不是通过引用传递的,而是通过共享传递的


6

它很大程度上可以追溯到Algol 68,部分原因是对C定义指针的方式的反应。

Algol 68定义了一个称为参考的概念。它与Pascal中的指针(例如)几乎相同。它是一个包含NIL或某些指定类型的其他单元格地址的单元格。您可以分配给引用,因此引用可以一次引用一个单元格,而在重新分配之后可以引用另一单元格。但是,它支持任何类似于C或C ++指针算术的东西。

但是,至少按照Algol 68的定义,引用是相当干净的概念,在实践中很难使用。大多数变量定义实际上都是引用,因此它们定义了一种简写形式的记号,以防止其完全失控,但是,仅是琐碎的使用,反而会很快变得笨拙。

例如,INT j := 7;编译器确实将like声明视为like声明REF INT j = NEW LOC INT := 7。所以,你在大陵68声明是正常的参考,然后初始化指这是在堆上分配东西,是(可选)初始化为包含一些指定的值。但是,与Java不同,它们至少试图让您为此维护合理的语法,而不是不断地进行类似的foo bar = new foo();尝试或试图向fib告知“我们的指针不是指针”。

Pascal及其大多数后代(直接的和...精神的)将Algol 68参考概念重命名为“指针”,但保持该概念本身基本相同:指针是一个变量,该变量持有或者nil分配的地址在堆上(即,至少如最初由Jensen和Wirth定义的那样,没有“ address-of”运算符,因此指针无法引用通常定义的变量)。尽管是“指针”,但不支持任何指针算法。

C和C ++为此添加了一些转折。首先,它们确实具有一个地址运算符,因此,指针不仅可以引用堆上分配的内容,还可以引用任何变量,而无论其分配方式如何。其次,它们在指针上定义算术-并将数组下标定义为指针算术的简写形式,因此在大多数C和C ++中,指针算术无处不在(几乎是不可避免的)。

当发明Java时,Sun显然认为“ Java没有指针”比“几乎所有Java都是指针,但这些指针大多类似于Pascal而不是C”是一种更简单,更干净的营销信息。由于他们认为“指针”不是一个可以接受的术语,因此他们需要其他内容,并疏通了“引用”,即使它们的引用与Algol 68引用有些微(在某些情况下不是那么微妙)。

尽管结果有所不同,但是C ++仍然存在大致相同的问题:“指针”一词已经为人所知并理解,因此他们需要一个不同的词来表示添加的其他事物,但这又指的是其他事物。与人们理解“指针”的含义有些不同。因此,即使它与Algol 68参考文献也有明显不同,他们也重新使用了“参考文献”一词。


自动取消引用使Algol 68中的引用特别痛苦。只要将其限制在一个级别,这就是方便。但是事实上,它可以与语法糖结合使用,从而掩盖了一些不为人知的眼睛,这使得它令人困惑。
AProgrammer 2014年

0

“引用类型”的概念是一个通用的概念,与“值类型”相反,它可以表示指针和引用(就C ++而言)。Java的引用是真的没有的句法开销指针->*取消引用。如果您研究JVM在C ++中的实现,那么它们实际上只是指针。同样,C#具有类似于Java的引用概念,但是它在函数参数上也具有指针和'ref'限定符,从而允许通过引用传递值类型,并避免像&在C ++中那样进行复制。


这个答案与这里先前的答案有何不同/更好?
蚊蚋
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.