Tại sao cả C ++ và Java đều sử dụng khái niệm về tài liệu tham khảo trực tuyến nhưng không có cùng ý nghĩa?


26

Trong C ++, một đối số tham chiếu đến một hàm cho phép hàm tạo tham chiếu tham chiếu đến một thứ khá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;
}

Tương tự, một đối số tham chiếu không đổi cho một hàm sẽ đưa ra lỗi thời gian biên dịch nếu chúng ta cố gắng thay đổi tham chiếu:

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

Bây giờ, với Java, các tài liệu nói rằng các đối số hàm của các kiểu không nguyên thủy là các tham chiếu. Ví dụ từ các tài liệu chính thức:

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

Sau đó, vòng tròn được gán một tham chiếu đến một đối tượng Circle mới với x = y = 0. Việc gán lại này không có tính lâu dài, tuy nhiên, vì tham chiếu được truyền theo giá trị và không thể thay đổi.

Đối với tôi điều này không giống như tất cả các tài liệu tham khảo C ++. Nó không giống với các tham chiếu C ++ thông thường vì bạn không thể làm cho nó tham chiếu đến một cái gì đó khác và nó không giống với các tham chiếu const C ++ bởi vì trong Java, mã sẽ thay đổi (nhưng thực sự không) tham chiếu không ném biên dịch lỗi thời gian.

Điều này tương tự nhiều hơn trong hành vi với con trỏ C ++. Bạn có thể sử dụng nó để thay đổi các giá trị đối tượng nhọn, nhưng bạn không thể thay đổi chính giá trị của con trỏ trong một hàm. Ngoài ra, như với các con trỏ C ++ (nhưng không phải với các tham chiếu C ++), trong Java, bạn có thể chuyển "null" làm giá trị cho một đối số như vậy.

Vì vậy, câu hỏi của tôi là: Tại sao Java sử dụng khái niệm "tham chiếu"? Có phải được hiểu rằng chúng không giống với tài liệu tham khảo C ++? Hay chúng thực sự giống với tài liệu tham khảo C ++ và tôi đang thiếu một cái gì đó?


10
Lưu ý rằng trong ví dụ C ++ đầu tiên của bạn, bạn không tạo tham chiếu "trỏ đến thứ khác". Thay vào đó, bạn đang thay đổi giá trị của biến các điểm tham chiếu thành. Điều này về mặt khái niệm tương tự như việc thay đổi trạng thái của đối tượng được tham chiếu với tham chiếu Java,
Xion

@Xion vâng, bây giờ tôi đã đọc bình luận của bạn, tôi hiểu ý của bạn và đồng ý với 98% những gì bạn nói :) Vấn đề trong Java là bạn phải có quyền truy cập vào bất kỳ và tất cả trạng thái của đối tượng để theo khái niệm đạt được những gì C ++ đơn giản làm bằng cách cho phép bạn đặt tham chiếu thành giá trị của một số tham chiếu khác.
Shivan Dragon

Câu trả lời:


48

Tại sao? Bởi vì, mặc dù thuật ngữ nhất quán thường tốt cho toàn bộ nghề nghiệp, các nhà thiết kế ngôn ngữ không luôn tôn trọng việc sử dụng ngôn ngữ của các nhà thiết kế ngôn ngữ khác, đặc biệt nếu những ngôn ngữ khác được coi là đối thủ cạnh tranh.

Nhưng thực sự, không sử dụng 'tài liệu tham khảo' là một lựa chọn rất tốt. "Tài liệu tham khảo" trong C ++ chỉ đơn giản là một cấu trúc ngôn ngữ để giới thiệu bí danh (tên thay thế cho chính xác cùng một thực thể) một cách rõ ràng. Mọi thứ sẽ rõ ràng hơn nhiều khi họ chỉ đơn giản gọi tính năng mới là "bí danh" ở vị trí đầu tiên. Tuy nhiên, tại thời điểm đó, khó khăn lớn là làm cho mọi người hiểu được sự khác biệt giữa con trỏ (yêu cầu hội thảo) và tài liệu tham khảo (không có), vì vậy điều quan trọng là nó được gọi là "con trỏ", và không phải vậy đặc biệt nhiều thuật ngữ để sử dụng.

Java không có con trỏ và tự hào về nó, vì vậy sử dụng "con trỏ" làm thuật ngữ là không có tùy chọn. Tuy nhiên, "tài liệu tham khảo" mà nó hoạt động khá giống như con trỏ của C ++ khi bạn vượt qua chúng - sự khác biệt lớn là bạn không thể thực hiện các thao tác cấp thấp khó khăn hơn (truyền, thêm ...) vào chúng , nhưng chúng dẫn đến chính xác cùng một ngữ nghĩa khi bạn chuyển các thẻ điều khiển cho các thực thể giống hệt với các thực thể chỉ đơn thuần là bằng nhau. Thật không may, thuật ngữ "con trỏ" mang rất nhiều liên kết cấp thấp tiêu cực đến mức không thể được cộng đồng Java chấp nhận.

Kết quả là cả hai ngôn ngữ sử dụng cùng một thuật ngữ mơ hồ cho hai thứ khá khác nhau, cả hai ngôn ngữ này có thể thu lợi từ một tên cụ thể hơn, nhưng cả hai ngôn ngữ này đều không thể thay thế sớm. Ngôn ngữ tự nhiên cũng vậy, đôi khi có thể làm nản lòng!


7
Cảm ơn, nghĩ về các tham chiếu C ++ là "bí danh" (cho cùng một giá trị / thể hiện) và các tham chiếu Java là "các con trỏ không có các hoạt động cấp thấp khó khăn hơn (truyền, thêm ...)" thực sự làm rõ mọi thứ cho tôi.
Shivan Dragon

14
Java không có con trỏ và tự hào về nó, vì vậy sử dụng "con trỏ" làm thuật ngữ là không có tùy chọn. Điều gì về NullPointerExceptionhoặc thực tế là JLS sử dụng thuật ngữ "con trỏ"?
Tobias Brandt

7
@TobiasBrandt: NullPointerExceptionkhông phải là một con trỏ, nhưng đó là một ngoại lệ chỉ ra rằng, ở cấp độ thấp hơn, một con trỏ NULL đã được thông qua / hủy đăng ký. Sử dụng Pointernhư một phần của ngoại lệ, nếu có bất cứ điều gì, làm tăng thêm hình ảnh khó chịu mà họ có. JLS phải đề cập đến các con trỏ, bởi vì VM của Java chủ yếu được viết bằng ngôn ngữ sử dụng chúng. Bạn không thể mô tả chính xác thông số kỹ thuật ngôn ngữ mà không mô tả cách thức hoạt động của các bộ phận bên trong
Elias Van Ootegem

3
@TobiasBrandt: Java sử dụng các con trỏ ở mọi nơi; đối tượng heap được tham chiếu thông qua một cái gì đó cho tất cả các mục đích thực tế là một con trỏ. Chỉ là Java không phơi bày bất kỳ hoạt động nào trên các loại con trỏ cho lập trình viên.
John Bode

2
Tôi thích thuật ngữ "ID đối tượng" cho các tham chiếu Java / .NET. Ở cấp độ bit, những thứ như vậy thường có thể được thực hiện như một cái gì đó tương tự như một con trỏ, nhưng không có gì đòi hỏi chúng phải như vậy. Điều quan trọng là ID đối tượng chứa bất kỳ loại thông tin nào mà framework / vm cần để xác định một đối tượng.
supercat

9

Một tài liệu tham khảo là một điều đề cập đến một điều khác. Các ngôn ngữ khác nhau đã quy định một ý nghĩa cụ thể hơn cho từ tham chiếu trực tiếp, thường là đối với một số thứ giống như một con trỏ mà không có tất cả các khía cạnh xấu. C ++ mô tả một ý nghĩa cụ thể, cũng như Java hoặc Perl.

Trong C ++, các tham chiếu giống như các bí danh (có thể được thực hiện thông qua một con trỏ). Điều này cho phép vượt qua bằng cách tham chiếu hoặc ra đối số .

Trong Java, các tham chiếu là các con trỏ ngoại trừ việc đây không phải là một khái niệm hợp nhất của ngôn ngữ: Tất cả các đối tượng là các tham chiếu, các kiểu nguyên thủy như số thì không. Họ không muốn nói con trỏ con trỏ Đây vì không có số học con trỏ và không có con trỏ thống nhất trong Java, nhưng họ muốn làm rõ rằng khi bạn truyền Objectmột đối số, đối tượng không được sao chép . Điều này cũng không vượt qua được bằng cách tham khảo , nhưng một cái gì đó giống như vượt qua bằng cách chia sẻ .


6

Nó chủ yếu quay trở lại Algol 68, và một phần là phản ứng chống lại cách C định nghĩa con trỏ.

Algol 68 định nghĩa một khái niệm gọi là tham chiếu. Nó khá giống với (ví dụ) một con trỏ trong Pascal. Đó là một ô chứa NIL hoặc địa chỉ của một số ô khác thuộc loại được chỉ định. Bạn có thể gán cho một tham chiếu, vì vậy một tham chiếu có thể tham chiếu đến một ô cùng một lúc và một ô khác sau khi được gán lại. Nó đã làm không , tuy nhiên, hỗ trợ bất cứ điều gì tương tự như C hoặc C ++ số học con trỏ.

Tuy nhiên, ít nhất là Algol 68 đã định nghĩa mọi thứ, các tài liệu tham khảo là khái niệm khá rõ ràng, khá vụng về để sử dụng trong thực tế. Hầu hết các định nghĩa biến thực sự là của các tài liệu tham khảo, vì vậy họ đã định nghĩa một ký hiệu tốc ký để giữ cho nó không bị mất kiểm soát, nhưng dù sao việc sử dụng tầm thường cũng có thể trở nên vụng về khá nhanh.

Ví dụ, một khai báo giống như INT j := 7;thực sự được trình biên dịch coi là khai báo như thế nào REF INT j = NEW LOC INT := 7. Vì vậy, những gì bạn khai báo trong Algol 68 là bình thường một tài liệu tham khảo, sau đó được khởi tạo để ám chỉ cái gì đó đã được cấp phát trên heap, và đó được (tùy chọn) khởi tạo để chứa một số giá trị nhất định. Tuy nhiên, không giống như Java, ít nhất họ đã cố gắng cho phép bạn duy trì cú pháp lành mạnh cho điều đó thay vì liên tục có những thứ như foo bar = new foo();hoặc cố gắng nói với các nhân vật về "con trỏ của chúng tôi không phải là con trỏ".

Pascal và hầu hết các hậu duệ của nó (cả trực tiếp và ... tâm linh) đã đổi tên khái niệm tham chiếu Algol 68 thành "con trỏ" nhưng về cơ bản vẫn giữ nguyên khái niệm: một con trỏ là một biến giữ nilhoặc địa chỉ của thứ gì đó bạn đã phân bổ trên heap (nghĩa là, ít nhất là theo định nghĩa ban đầu của Jensen và Wirth, không có toán tử "address-of", vì vậy không có cách nào để con trỏ tham chiếu đến một biến được xác định thông thường). Mặc dù là "con trỏ", không có số học con trỏ nào được hỗ trợ.

C và C ++ đã thêm một vài vòng xoắn vào đó. Thứ nhất, họ làm có một địa chỉ-của nhà điều hành, do đó, một con trỏ có thể tham khảo không chỉ để một cái gì đó cấp phát trên heap, nhưng đối với bất kỳ biến, bất kể như thế nào nó được phân bổ. Thứ hai, họ định nghĩa số học trên các con trỏ - và định nghĩa các chỉ số mảng về cơ bản chỉ là một ký hiệu viết tắt cho số học con trỏ, vì vậy số học con trỏ là phổ biến (bên cạnh không thể tránh khỏi) trong hầu hết C và C ++.

Khi Java được phát minh, Sun rõ ràng nghĩ rằng "Java không có con trỏ" là một thông điệp tiếp thị đơn giản, gọn gàng hơn: "gần như mọi thứ trong Java là một con trỏ, nhưng những con trỏ này hầu hết giống như Pascal thay vì C". Vì họ đã quyết định "con trỏ" không phải là một thuật ngữ có thể chấp nhận được, nên họ cần một thứ khác và thay vào đó là "tham chiếu", mặc dù các tham chiếu của họ rất tinh tế (và trong một số trường hợp không quá khác biệt) so với các tham chiếu của Algol 68.

Mặc dù nó xuất hiện hơi khác, nhưng C ++ bị mắc kẹt với cùng một vấn đề: từ "con trỏ" đã được biết và hiểu, vì vậy họ cần một từ khác cho thứ khác này mà họ đã thêm vào để nói đến một thứ khác, nhưng ngược lại một chút khác với những gì mọi người hiểu "con trỏ" có nghĩa là. Vì vậy, mặc dù nó cũng khác biệt đáng kể so với tham chiếu Algol 68, họ cũng sử dụng lại thuật ngữ "tham chiếu".


Điều khiến tài liệu tham khảo trong Algol 68 đặc biệt đau đớn là hội thảo tự động. Đó là một cái gì đó thuận tiện miễn là nó bị giới hạn ở một cấp độ. Nhưng thực tế là nó có thể được thực hiện nhiều lần kết hợp với đường cú pháp che giấu một số tham chiếu đến đôi mắt không biết khiến nó trở nên khó hiểu.
AProgrammer

0

Notion của 'kiểu tham chiếu' là một kiểu chung, nó có thể biểu thị cả con trỏ và tham chiếu (theo C ++), trái ngược với type loại giá trị '. Các tài liệu tham khảo của Java thực sự là con trỏ mà không cần cú pháp tổng hợp của ->một cuộc hội thảo *. Nếu bạn xem xét triển khai của JVM trong C ++, thì chúng thực sự là con trỏ trần. Ngoài ra, C # có một khái niệm về các tham chiếu tương tự như Java, nhưng nó cũng có các con trỏ và định tính 'ref' trên các tham số hàm, cho phép truyền các loại giá trị bằng cách tham chiếu và tránh sao chép giống như &trong C ++.


Làm thế nào là câu trả lời này khác nhau / tốt hơn so với câu trả lời trước đây ở đây?
gnat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.