Mengapa C ++ dan Java sama-sama menggunakan gagasan "referensi" tetapi tidak dalam arti yang sama?

26

Dalam C ++ argumen referensi ke fungsi memungkinkan fungsi untuk membuat referensi merujuk ke sesuatu yang lain:

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

Secara analogi, argumen referensi konstan ke suatu fungsi akan menimbulkan kesalahan waktu kompilasi jika kami mencoba mengubah referensi:

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

Sekarang, dengan Java, dokumen mengatakan bahwa argumen fungsi tipe non-primitif adalah referensi. Contoh dari dokumen resmi:

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

Kemudian lingkaran diberi referensi ke objek Lingkaran baru dengan x = y = 0. Penugasan ulang ini tidak memiliki permanen, karena referensi dilewatkan oleh nilai dan tidak dapat berubah.

Bagi saya ini sama sekali tidak seperti referensi C ++. Itu tidak menyerupai referensi C ++ biasa karena Anda tidak dapat membuatnya merujuk ke sesuatu yang lain, dan itu tidak menyerupai referensi C ++ const karena di Jawa, kode yang akan berubah (tetapi sebenarnya tidak) referensi tidak membuang kompilasi kesalahan waktu

Ini lebih mirip dalam perilaku dengan pointer C ++. Anda bisa menggunakannya untuk mengubah nilai objek runcing, tetapi Anda tidak bisa mengubah nilai pointer itu sendiri dalam suatu fungsi. Juga, seperti dengan pointer C ++ (tetapi tidak dengan referensi C ++), di Java Anda bisa memberikan "null" sebagai nilai untuk argumen seperti itu.

Jadi pertanyaan saya adalah: Mengapa Java menggunakan gagasan "referensi"? Apakah harus dipahami bahwa mereka tidak menyerupai referensi C ++? Atau apakah mereka memang sangat mirip dengan referensi C ++ dan saya kehilangan sesuatu?

Shivan Dragon
sumber
10
Perhatikan bahwa dalam contoh C ++ pertama Anda, Anda tidak membuat referensi "arahkan ke yang lain". Sebagai gantinya, Anda mengubah nilai variabel dari titik referensi. Secara konseptual ini mirip dengan bermutasi keadaan objek yang disebut dengan referensi Java,
Xion
@Xion ya, sekarang saya sudah membaca komentar Anda, saya melihat apa yang Anda maksud dan setuju dengan 98% dari apa yang Anda katakan :) Masalahnya di Jawa adalah bahwa Anda harus memiliki akses ke setiap dan semua keadaan objek agar dapat secara konseptual mencapai apa yang C ++ lakukan dengan membiarkan Anda menetapkan referensi ke nilai beberapa referensi lain.
Shivan Dragon

Jawaban:

48

Mengapa? Karena, meskipun terminologi yang konsisten umumnya baik untuk keseluruhan profesi, perancang bahasa tidak selalu menghargai penggunaan bahasa perancang bahasa lain, terutama jika bahasa lain itu dianggap sebagai pesaing.

Tapi sungguh, tidak menggunakan 'referensi' adalah pilihan yang sangat baik. "Referensi" dalam C ++ hanyalah sebuah konstruksi bahasa untuk memperkenalkan alias (nama alternatif untuk entitas yang persis sama) secara eksplisit. Hal-hal akan jauh lebih jelas dari mereka hanya menyebut fitur baru "alias" di tempat pertama. Namun, pada saat itu kesulitan terbesarnya adalah membuat semua orang memahami perbedaan antara pointer (yang memerlukan dereferencing) dan referensi (yang tidak), jadi yang penting itu disebut sesuatu selain "penunjuk", dan tidak begitu khususnya istilah apa yang digunakan.

Java tidak memiliki pointer, dan bangga karenanya, jadi menggunakan "pointer" sebagai sebuah istilah bukanlah pilihan. Namun, "referensi" yang berperilaku cukup sedikit seperti pointer C ++ lakukan ketika Anda menyebarkannya - perbedaan besar adalah bahwa Anda tidak dapat melakukan operasi tingkat rendah yang lebih jahat (casting, menambahkan ...) pada mereka , tetapi mereka menghasilkan semantik yang sama persis ketika Anda menyerahkan gagang ke entitas yang identik vs entitas yang kebetulan sama. Sayangnya, istilah "penunjuk" membawa begitu banyak asosiasi tingkat rendah yang negatif sehingga tidak mungkin diterima oleh masyarakat Jawa.

Hasilnya adalah bahwa kedua bahasa menggunakan istilah samar yang sama untuk dua hal yang agak berbeda, keduanya dapat mengambil untung dari nama yang lebih spesifik, tetapi keduanya tidak akan diganti dalam waktu dekat. Bahasa alami juga terkadang membuat frustasi!

Kilian Foth
sumber
7
Terima kasih, memikirkan referensi C ++ sebagai "alias" (untuk nilai / contoh yang sama) dan referensi Java sebagai "petunjuk tanpa operasi tingkat rendah yang lebih jahat (casting, tambah ...)" benar-benar menjelaskan hal-hal untuk saya.
Shivan Dragon
14
Java tidak memiliki pointer, dan bangga karenanya, jadi menggunakan "pointer" sebagai sebuah istilah bukanlah pilihan. Bagaimana NullPointerExceptiondengan fakta bahwa JLS menggunakan istilah "pointer"?
Tobias Brandt
7
@TobiasBrandt: NullPointerExceptionbukan penunjuk, tetapi merupakan pengecualian yang menunjukkan bahwa, pada tingkat yang lebih rendah, penunjuk NULL diteruskan / ditinjau ulang. Menggunakan Pointersebagai bagian dari pengecualian, jika ada, menambah gambar buruk yang mereka miliki. JLS harus menyebutkan pointer, karena VM Java terutama ditulis dalam bahasa yang menggunakannya. Anda tidak dapat mendeskripsikan spesifikasi bahasa secara akurat tanpa menjelaskan cara kerja internal
Elias Van Ootegem
3
@TobiasBrandt: Java menggunakan pointer di semua tempat; objek tumpukan direferensikan melalui sesuatu yang untuk semua tujuan praktis adalah pointer. Hanya saja Java tidak mengekspos operasi apa pun pada tipe pointer ke programmer.
John Bode
2
Saya lebih suka istilah "ID objek" untuk referensi Java / .NET. Pada tingkat bit, hal-hal seperti itu biasanya dapat diimplementasikan sebagai sesuatu yang menyerupai pointer, tetapi tidak ada yang mengharuskannya. Yang penting adalah bahwa ID objek berisi informasi apa pun yang dibutuhkan kerangka kerja / vm untuk mengidentifikasi objek.
supercat
9

Referensi adalah hal yang mengacu pada hal lain. Berbagai bahasa telah memberikan makna yang lebih spesifik pada kata "referensi", biasanya untuk "sesuatu seperti pointer tanpa semua aspek buruk". C ++ menganggap makna tertentu, seperti halnya Java atau Perl.

Dalam C ++, referensi lebih seperti alias (yang dapat diimplementasikan melalui pointer). Ini memungkinkan lewat referensi atau keluar argumen .

Di Jawa, referensi adalah petunjuk kecuali bahwa ini bukan konsep bahasa yang ditegaskan: Semua objek adalah referensi, tipe primitif seperti angka tidak. Mereka tidak ingin mengatakan "pointer" karena tidak ada aritmatika pointer dan tidak ada pointer reified di Jawa, tetapi mereka ingin menjelaskan bahwa ketika Anda memberikan Objectargumen, objek tidak disalin . Ini juga bukan lewat referensi , tetapi sesuatu yang lebih seperti lulus dengan berbagi .

amon
sumber
6

Sebagian besar kembali ke Algol 68, dan sebagian ke reaksi terhadap cara C mendefinisikan pointer.

Algol 68 mendefinisikan konsep yang disebut referensi. Itu hampir sama dengan (misalnya) pointer di Pascal. Itu adalah sel yang berisi NIL atau alamat beberapa sel lain dari beberapa jenis tertentu. Anda dapat menetapkan referensi, sehingga referensi dapat merujuk ke satu sel pada satu waktu, dan sel yang berbeda setelah dipindahkan. Namun, itu tidak mendukung apa pun yang analog dengan aritmetika pointer C atau C ++.

Paling tidak sebagaimana Algol 68 mendefinisikan hal-hal, bagaimanapun, referensi adalah konsep yang cukup bersih yang cukup canggung untuk digunakan dalam praktik. Sebagian besar definisi variabel sebenarnya dari referensi, sehingga mereka mendefinisikan notasi steno untuk mencegahnya menjadi benar-benar lepas kendali, tetapi lebih dari penggunaan sepele bisa menjadi canggung dengan cukup cepat pula.

Misalnya, deklarasi suka INT j := 7;benar-benar diperlakukan oleh kompilator sebagai deklarasi suka REF INT j = NEW LOC INT := 7. Jadi, apa yang Anda nyatakan dalam Algol 68 biasanya adalah referensi, yang kemudian diinisialisasi untuk merujuk pada sesuatu yang dialokasikan pada heap, dan yang (secara opsional) diinisialisasi untuk mengandung beberapa nilai tertentu. Tidak seperti Java, bagaimanapun, mereka setidaknya mencoba membiarkan Anda mempertahankan sintaksis waras untuk itu daripada terus-menerus memiliki hal-hal seperti foo bar = new foo();atau mencoba untuk memberitahu fibs tentang "pointer kami bukan pointer."

Pascal dan sebagian besar turunannya (baik langsung maupun ... spiritual) mengganti nama konsep referensi Algol 68 menjadi "pointer" tetapi mempertahankan konsep itu sendiri pada dasarnya tetap sama: pointer adalah variabel yang menahan nil, atau alamat dari sesuatu yang Anda alokasikan pada heap (yaitu, setidaknya seperti yang didefinisikan oleh Jensen dan Wirth, tidak ada operator "address-of", jadi tidak ada cara bagi pointer untuk merujuk ke variabel yang didefinisikan secara normal). Meskipun "pointer", aritmatika pointer tidak didukung.

C dan C ++ menambahkan beberapa tikungan untuk itu. Pertama, mereka memang memiliki alamat-operator, sehingga pointer dapat merujuk tidak hanya pada sesuatu yang dialokasikan pada heap, tetapi juga pada variabel apa pun, terlepas dari bagaimana dialokasikan. Kedua, mereka mendefinisikan aritmatika pada pointer - dan mendefinisikan subscript array sebagai dasarnya hanya notasi singkat untuk aritmatika pointer, sehingga aritmatika pointer ada di mana-mana (di samping tidak dapat dihindari) di sebagian besar C dan C ++.

Ketika Java ditemukan, Sun rupanya mengira "Java tidak memiliki pointer" adalah pesan pemasaran yang lebih sederhana dan lebih bersih daripada: "hampir semua yang ada di Jawa adalah pointer, tetapi pointer ini kebanyakan seperti Pascal, bukan C." Karena mereka memutuskan "penunjuk" bukan istilah yang dapat diterima, mereka membutuhkan sesuatu yang lain, dan malah "mengeruk" referensi, meskipun referensi mereka secara halus (dan dalam beberapa kasus tidak begitu halus) berbeda dari referensi Algol 68.

Meskipun itu keluar agak berbeda, C ++ terjebak dengan masalah yang kira-kira sama: kata "pointer" sudah dikenal dan dipahami, jadi mereka membutuhkan kata yang berbeda untuk hal yang berbeda yang mereka tambahkan yang merujuk ke sesuatu yang lain, tetapi sebaliknya cukup sedikit berbeda dari apa yang orang pahami "penunjuk" artinya. Jadi, meskipun itu juga sangat berbeda dari referensi Algol 68, mereka juga menggunakan istilah "referensi".

Jerry Coffin
sumber
Apa yang membuat referensi dalam Algol 68 sangat menyakitkan adalah dereferencing otomatis. Itu sesuatu yang nyaman asalkan terbatas pada satu level. Tetapi fakta bahwa itu bisa dilakukan beberapa kali dikombinasikan dengan gula sintaksis yang menyembunyikan beberapa referensi ke mata yang tidak sadar membuatnya membingungkan.
Pemrogram
0

Notion dari 'tipe referensi' adalah yang generik, dapat mengekspresikan pointer dan referensi (dalam hal C ++), sebagai lawan dari 'tipe nilai'. Referensi Jawa benar-benar pointer tanpa biaya overhead sintaksis dari ->sebuah *dereferencing. Jika Anda melihat implementasi JVM di C ++, mereka benar-benar petunjuk kosong. Juga, C # memiliki gagasan referensi yang mirip dengan Java, tetapi juga memiliki pointer dan 'ref' kualifikasi pada parameter fungsi, yang memungkinkan untuk meneruskan tipe-nilai dengan referensi dan menghindari penyalinan seperti &di C ++.

Hertz
sumber
bagaimana jawaban ini berbeda / lebih baik dari jawaban sebelumnya di sini?
nyamuk