C ++ ve Java neden her ikisi de “referans” kavramını kullanıyor ama aynı anlamda kullanmıyor?


26

C ++ 'da bir fonksiyona yapılan referans argümanı, fonksiyonun referansı başka bir şeye yönlendirmesini sağlar:

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

Benzer şekilde, referansı değiştirmeye çalışırsak, bir fonksiyonun sabit bir referans argümanı derleme zamanı hatası verir:

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

Şimdi, Java ile birlikte, dokümanlar, ilkel olmayan türlerin argümanlarının referans olduğunu söylüyorlar. Resmi belgelerden örnek:

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

Sonra çembere, x = y = 0 olan yeni bir Circle nesnesine bir başvuru atanır.

Bana göre bu C ++ referanslarına hiç benzemiyor. Düzenli C ++ referanslarına benzemiyor çünkü başka bir şeye başvuru yapamıyorsunuz ve C ++ const referanslarına benzemiyor çünkü Java'da değişecek kod (ancak gerçekten yok) başvuru derleme yapmıyor -zaman hatası.

Bu davranış olarak C ++ işaretçilerine benzer. Sivri uçlu nesnelerin değerlerini değiştirmek için kullanabilirsiniz, ancak işaretleyicinin değerini bir işlevde değiştiremezsiniz. Ayrıca, C ++ işaretçilerinde olduğu gibi (ancak C ++ referanslarıyla değil), Java'da böyle bir argüman için "null" değerini geçebilirsiniz.

Öyleyse sorum şu: Java neden "başvuru" kavramını kullanıyor? C ++ referanslarına benzemedikleri anlaşılıyor mu? Yoksa C ++ referanslarına gerçekten benziyorlar mı ve ben bir şeyleri özlüyorum?


10
İlk C ++ örneğinizde, referansı "başka bir şeye işaret" yapmıyorsunuz. Bunun yerine, referansın gösterdiği değişkenin değerini değiştiriyorsunuz. Bu kavramsal olarak, Java referansı ile atıfta bulunulan nesnenin durumunu değiştirmeye benzer,
Xion

@Xion evet, şimdi yorumunuzu okudum, ne demek istediğinizi anlamadığınızı ve% 98 ile aynı fikirdeyim :) referansı başka bir referansın değerine ayarlamanıza izin vererek C ++ 'ın basitçe ne yaptığını elde edin.
Shivan Dragon

Yanıtlar:


48

Niye ya? Çünkü tutarlı terminoloji genellikle tüm meslek için iyi olsa da, dil tasarımcıları, özellikle diğer diller rakip olarak algılanırsa, diğer dil tasarımcılarının dil kullanımına her zaman saygı duymazlar.

Fakat gerçekten, 'referans' kullanımı ne çok iyi bir seçim değildi. C ++ 'da "Referanslar" açıkça takma adları (tam olarak aynı varlık için alternatif isimler) tanıtmak için kullanılan bir dildir . İşler, her şeyden önce, yeni özellik "takma adlar" olarak adlandırılmaları çok daha net olurdu. Ancak, o zaman en büyük zorluk, herkesin işaretçiler (referansları kaldırmayı gerektiren) ve referanslar (ki istemeyen) arasındaki farkı anlamasını sağlamaktı, bu yüzden önemli olan, "işaretçi" den başka bir şey olarak adlandırılmasıydı. çok özel olarak hangi terimi kullanacaksınız.

Java'da imleç yok ve bununla gurur duyuyor, dolayısıyla "pointer" ı bir terim olarak kullanmak seçenek değildi. Ancak, C ++ işaretçilerinin etrafta dolaştığında yaptıkları gibi, "referanslar" oldukça fazla davranıyor - en büyük fark, daha düşük seviyeli işlemleri (döküm, ekleme ...) yapamamanız. , ancak, tam olarak aynı olan varlıklara karşı, yalnızca eşit olan varlıklara eşlik ederken, aynı anlamsallıkla sonuçlanırlar. Ne yazık ki, "işaretçi" terimi, Java topluluğu tarafından kabul edilme olasılığı düşük olan çok sayıda düşük düzeyli dernek taşımaktadır.

Sonuç, her iki dilin de aynı belirsiz terimi, her ikisi de daha belirli bir addan kar elde edebileceği, ancak ikisinin de yakın zamanda değiştirilmemesi muhtemel olan iki farklı şey için kullanmasıdır. Doğal dil de bazen sinir bozucu olabilir!


7
Teşekkürler, C ++ referanslarını "takma adlar" (aynı değer / örnek için) ve Java referanslarını "daha düşük seviyeli düşük işlemler olmadan işaretçiler (döküm, ekleme ...)" olarak düşünmek benim için gerçekten netleşiyor.
Shivan Dragon

14
Java'da imleç yok ve bununla gurur duyuyor, dolayısıyla "pointer" ı bir terim olarak kullanmak seçenek değildi. Peki NullPointerExceptionya JLS'nin "pointer" terimini kullanması?
Tobias Brandt

7
@ TobiasBrandt: NullPointerExceptionbir işaretçi değildir, ancak daha düşük bir düzeyde NULL işaretleyicinin geçtiğini / reddedildiğini gösteren bir istisnadır. Pointerİstisna parçası olarak kullanmak , eğer varsa, sahip oldukları kötü görüntüye katkıda bulunur. JLS işaretçilerden bahsetmek zorundadır , çünkü Java'nın VM'i çoğunlukla onları kullanan bir dilde yazılmıştır. Dilin özelliklerini, içerideki kişilerin nasıl çalıştığını açıklamadan doğru bir şekilde tanımlayamazsınız
Elias Van Ootegem

3
@ TobiasBrandt: Java her yerde işaretçiler kullanır; Öbek nesnelerine, tüm pratik amaçlar için bir işaretçi olan bir şey başvurulur. Sadece Java, işaretçi tiplerindeki herhangi bir işlemi programlayıcıya maruz bırakmaz.
John Bode

2
Java / .NET referansları için "nesne kimliği" terimini tercih ediyorum. Bit seviyesinde, bu tür şeyler tipik olarak bir işaretçiye benzeyen bir şey olarak uygulanabilir, fakat hiçbir şey onların olmasını gerektirmez. Önemli olan, bir nesne kimliğinin, çerçeve / vm'nin bir nesneyi tanımlamak için ihtiyaç duyduğu her türlü bilgiyi içermesidir.
supercat,

9

Referans başka bir şeye işaret eden bir şeydir. Çeşitli diller “referans” kelimesine, genellikle “tüm kötü yönleri olmayan bir işaretçi gibi bir şeye” daha spesifik bir anlam atfetmiştir. C ++, Java veya Perl gibi, belirli bir anlam ifade eder.

C ++ 'da referanslar daha çok takma adlara benzer (bir işaretçi ile uygulanabilir). Bu, referans yoluyla veya argümanlarla iletmeyi sağlar .

Java'da referanslar, dilin birleştirilmiş bir konsepti olmadığı dışında işaretçilerdir: Tüm nesneler referanslardır, sayılar gibi ilkel türler değildir. İşaretçi aritmetiği olmadığı ve Java'da birleştirilmiş işaretçiler olmadığı için “işaretçi” demek istemiyorlar, ancak Objectbir argüman olarak geçirdiğinizde nesnenin kopyalanmadığını açıkça belirtmek istiyorlar . Bu aynı zamanda referans yoluyla geçmez , fakat paylaşarak geçerek daha çok benzer bir şeydir .


6

Büyük ölçüde Algol 68'e, kısmen de C işaretçilerinin tanımlanma şeklindeki tepkimeye geri döner.

Algol 68 referans olarak adlandırılan bir kavramı tanımlamıştır. Pascal'da (bir örnek için) bir işaretçi ile hemen hemen aynıydı. NIL ya da belirtilen tipte başka bir hücrenin adresini içeren bir hücreydi. Bir referansa atayabilirsiniz, böylece bir referans bir kerede bir hücreye ve atandıktan sonra farklı bir hücreye başvurabilir. Bu yaptığımız değil , ancak, C veya C ++ işaretçi aritmetik destek şey benzer.

En azından Algol 68'in tanımladığı gibi, referanslar pratikte kullanımı oldukça sakarcaydı. Değişken tanımların çoğu aslında referanslardı, bu yüzden tamamen elden uzak durmasını engellemek için bir kısa yol gösterimi tanımladılar, ancak önemsiz kullanımdan daha fazlası yine de oldukça sakarlanabilirdi.

Örneğin, böyle bir bildiri INT j := 7;derleyici tarafından gerçekten bir bildiri olarak işlendi REF INT j = NEW LOC INT := 7. Yani, Algol 68'de bildirdiğiniz şey normalde bir referanstı, bu daha sonra öbek üzerinde tahsis edilen bir şeye gönderme yapmak için başlatıldı ve bu (isteğe bağlı olarak) belirli bir değeri içerecek şekilde başlatıldı. Bununla birlikte, Java'dan farklı olarak, en azından foo bar = new foo();"işaretçilerimiz işaretçiler değildir" hakkında sürekli olarak fibere benzer şeyler söylemeye ya da anlatmaya çalışmak yerine bunun akılda kalıcı sözdizimini korumanıza izin verdiler.

Pascal ve soyundan gelenlerin çoğu (hem doğrudan hem de manevi) Algol 68 referans kavramını "işaretçi" olarak yeniden adlandırdı, ancak kavramın temelde aynı kalmasını sağladı: işaretçi nil, ya da ayırdığınız bir şeyin adresini tutan bir değişkendi öbek üzerinde (yani, en azından orijinal olarak Jensen ve Wirth tarafından tanımlandığı gibi, "adres" operatörü yoktu, bu nedenle bir işaretçinin normal olarak tanımlanmış bir değişkene başvurması için bir yol yoktu). "İşaretçiler" olmasına rağmen, işaretçi aritmetiği desteklenmiyordu.

C ve C ++ buna birkaç büküm ekledi. İlk olarak, bunu bir işaretçi yığın tahsis şeye sadece başvurabilmeniz için, bir adres-operatörü var, ama herhangi bir değişkene bakılmaksızın bu tahsis nasıl yapıldığını gösteren. İkincisi, işaretçiler üzerindeki aritmetiği tanımlarlar - ve dizi abonelerini temelde sadece işaretçi aritmetiği için kısa bir gösterim olarak tanımlarlar, bu nedenle işaretçi aritmetiği çoğu C ve C ++ 'da her yerde bulunur (kaçınılmaz olanın yanında).

Java, icat edilirken, Sun’ın görünüşte "Java’da imleci yok" ’dan daha basit ve daha temiz bir pazarlama mesajı olduğunu düşündü:" Java’daki neredeyse her şey bir işaretçidir, ancak bu işaretçiler çoğunlukla C’nin yerine Pascal’lar gibidir. " "İşaretçi" nin kabul edilebilir bir terim olmadığına karar verdiklerinden, başka bir şeye ihtiyaçları vardı ve referansları zekice (ve bazı durumlarda çok kük olmayan) Algol 68 referanslarından farklı olsalar bile, "referans" ı aradılar.

Her ne kadar farklı bir şekilde ortaya çıksa da, C ++ hemen hemen aynı problemle karşılaştı: “pointer” kelimesi zaten biliniyordu ve anlaşıldı, bu yüzden başka bir şeye atıfta bulundukları bu farklı şey için farklı bir kelimeye ihtiyaç duydular; insanların demek istediklerinin "göstericisini" anladıklarından biraz farklı. Dolayısıyla, bir Algol 68 referansından gözle görülür şekilde farklı olmasına rağmen, "referans" terimini de yeniden kullandılar.


Algol 68'de özellikle acı verici bir referans yapan, otomatik yeniden düzenlemedir. Bu, bir seviye ile sınırlı olduğu sürece uygun bir şey. Ancak habersiz gözlere yapılan bazı referansları gizleyen sözdizimsel şeker ile bir kaç kez yapılabileceği gerçeği kafa karıştırıcı yaptı.
AProgrammer,

0

'Referans türü' kavramı genel bir ifadedir, 'değer türü' yerine hem işaretçi hem de referansı (C ++ cinsinden) ifade edebilir. Java'nın referanslar gerçekten sözdizimsel yük olmaksızın işaretçileri ->bir *dereferencing. JVM'nin C ++ 'daki uygulamasına bakarsanız, gerçekten çıplak işaretçilerdir. Ayrıca, C #, Java'lara benzer bir referans kavramına sahiptir, ancak aynı zamanda, &C + 'da olduğu gibi kopyalamaktan kaçınarak değer türlerini referans alarak iletmek ve fonksiyon parametrelerine ilişkin işaretçiler ve' ref 'niteleyicileri içerir .


Bu cevap burada önceki cevaplardan nasıl farklı / daha iyi?
tatarcık
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.