C ++ ve Java'daki “genel” türler arasındaki farklar nelerdir?


Yanıtlar:


144

Aralarında büyük bir fark var. C ++ 'da, genel tip için bir sınıf veya arabirim belirtmeniz gerekmez. Bu nedenle, daha gevşek bir yazımın uyarısı ile gerçekten genel fonksiyonlar ve sınıflar oluşturabilirsiniz.

template <typename T> T sum(T a, T b) { return a + b; }

Yukarıdaki yöntem aynı türden iki nesne ekler ve "+" operatörüne sahip herhangi bir T türü için kullanılabilir.

Java'da, geçirilen nesneler üzerindeki yöntemleri çağırmak istiyorsanız bir tür belirtmeniz gerekir:

<T extends Something> T sum(T a, T b) { return a.add ( b ); }

C ++ 'da genel işlevler / sınıflar yalnızca üstbilgilerde tanımlanabilir, çünkü derleyici farklı türler için farklı işlevler üretir (çağrıldığı gibi). Yani derleme daha yavaş. Java'da derlemenin büyük bir cezası yoktur, ancak Java, genel türün çalışma zamanında silindiği "silme" adlı bir teknik kullanır, bu nedenle çalışma zamanında Java gerçekten çağırır ...

Something sum(Something a, Something b) { return a.add ( b ); }

Java'da genel programlama gerçekten kullanışlı değil, yeni foreach yapısına yardımcı olmak için sadece biraz sözdizimsel şeker.

DÜZENLEME: yukarıdaki kullanışlılık hakkındaki görüş genç bir ben tarafından yazılmıştır. Java'nın jenerikleri elbette tip güvenliğine yardımcı olur.


27
Sadece ayrıntılı bir sözdizimsel şeker olduğu konusunda kesinlikle doğrudur.
alphazero

31
Sadece sözdizimsel şeker değildir. Derleyici bu bilgileri türleri denetlemek için kullanır. Her ne kadar bilgi çalışma zamanında mevcut olmasa da, derlenen sadece "sözdizimsel şeker" kullanır bir şey demezdim. Eğer böyle derseniz, o zaman C sadece montaj için sözdizimsel şekerdir ve bu sadece makine kodu için sözdizimsel şekerdir :)
dtech

42
Bence sözdizimsel şeker faydalıdır.
poitroae

5
Bir jenerik örneği oluşturmak için kullanabileceğiniz önemli bir farkı kaçırdınız. C ++ 'da <int N> şablonunu kullanmak ve bunu başlatmak için kullanılan herhangi bir sayı için farklı bir sonuç elde etmek mümkündür. Derleme zamanı meta programlaması için kullanılır. Cevap gibi: stackoverflow.com/questions/189172/c-templates-turing-complete
stonemetal

2
Sen do not ya şeklinde, 'bir türünü belirtmek' zorunda extendsya super. Cevap yanlış,
Lorne Marquis

124

Java Generics, C ++ şablonlarından çok farklıdır.

Temelde C ++ şablonlarında temelde yüceltilmiş bir önişlemci / makro seti vardır ( Not: bazı insanlar benzetmeyi anlayamıyor gibi göründüğünden, şablon işlemenin bir makro olduğunu söylemiyorum). Java'da temel olarak Nesnelerin kazan plakası dökümünü en aza indirmek için sözdizimsel şekerdir. İşte Java generics vs C ++ şablonlarına oldukça iyi bir giriş .

Bu noktayı ele almak için: bir C ++ şablonu kullandığınızda, tıpkı bir #definemakro kullanıyormuşsunuz gibi, kodun başka bir kopyasını oluşturursunuz . Bu, intşablon tanımlarında dizilerin boyutlarını belirleyen parametreler ve benzeri şeyler yapmanıza olanak tanır .

Java böyle çalışmaz. Java'da tüm nesneler java.lang.Object'ten uzanır, Generics öncesi, şöyle bir kod yazarsınız:

public class PhoneNumbers {
  private Map phoneNumbers = new HashMap();

  public String getPhoneNumber(String name) {
    return (String)phoneNumbers.get(name);
  }

  ...
}

çünkü tüm Java toplama türleri Object'i temel türü olarak kullandı, böylece içine bir şey koyabilirsiniz. Java 5 yuvarlanır ve jenerikler ekler, böylece aşağıdakileri yapabilirsiniz:

public class PhoneNumbers {
  private Map<String, String> phoneNumbers = new HashMap<String, String>();

  public String getPhoneNumber(String name) {
    return phoneNumbers.get(name);
  }

  ...
}

Ve işte hepsi Java Generics: nesneleri dökmek için sarıcılar. Java Generics rafine edilmemiş olmasıdır. Tip silme kullanırlar. Bu karar, Java Generics'in geriye dönük uyumluluğu kırmak istemedikleri parçada çok geç gelmesi nedeniyle verildi (a Map<String, String>istendiğinde kullanılabilir Map). Tip silme kullanılmadığı durumlarda, .Net / C # ile karşılaştırın hangi farklılıklara her türlü (örneğin siz ve ilkel türlerini kullanabilirsiniz için potansiyel müşteriler IEnumerableve IEnumerable<T>birbirlerine hiçbir ilişkisi olmayan).

Ve Java 5+ derleyicisiyle derlenen jenerikleri kullanan bir sınıf JDK 1.4'te kullanılabilir (Java 5+ gerektiren başka özellikler veya sınıflar kullanmadığı varsayılarak).

Bu yüzden Java Generics sözdizimsel şeker olarak adlandırılır .

Ancak jeneriklerin nasıl yapılacağına dair bu karar çok derin etkilere sahiptir, böylece (mükemmel) Java Generics SSS , insanların Java Generics ile ilgili birçok soruya cevap vermek için ortaya çıktı.

C ++ şablonlarında Java Generics'in kullanmadığı birçok özellik vardır:

  • İlkel tür argümanlarının kullanımı.

    Örneğin:

    template<class T, int i>
    class Matrix {
      int T[i][i];
      ...
    }

    Java, jeneriklerde ilkel tür argümanlarının kullanılmasına izin vermez.

  • Kullanımı varsayılan tür argümanları Java bir özellik ben özledim ama bunun için uyumluluk nedenleri geriye vardır;

  • Java, argümanların sınırlanmasına izin verir.

Örneğin:

public class ObservableList<T extends List> {
  ...
}

Farklı argümanlara sahip şablon çağrılarının gerçekten farklı türler olduğunu gerçekten vurgulamak gerekir. Statik üyeleri paylaşmıyorlar bile. Java'da durum böyle değildir.

Jeneriklerle olan farklılıkların yanı sıra, bütünlük için, burada C ++ ve Java'nın (ve başka birinin ) temel bir karşılaştırması .

Java'da Düşünmeyi de önerebilirim . Bir C ++ programcısı olarak nesneler gibi kavramların birçoğu zaten ikinci doğa olacaktır, ancak ince farklılıklar vardır, bu yüzden parçaları yağlasanız bile bir giriş metnine sahip olmak faydalı olabilir.

Java öğrenirken öğreneceğiniz birçok şey tüm kütüphanelerdir (her ikisi de standart - JDK'da gelenler - ve Bahar gibi yaygın olarak kullanılan şeyleri içeren standart olmayan). Java sözdizimi, C ++ sözdiziminden daha ayrıntılıdır ve çok fazla C ++ özelliğine (örn. Operatör aşırı yüklenmesi, çoklu kalıtım, yıkıcı mekanizma, vb.) Sahip değildir, ancak bu kesinlikle C ++ 'ın bir alt kümesi değildir.


1
Kavramda eşdeğer değillerdir. Bunun en iyi örneği merakla yinelenen şablon desenidir. İkinci en iyi politika odaklı tasarım. Üçüncüsü, C ++ 'nin integral sayılarının köşeli parantez içinde (myArray <5>) geçirilmesine izin vermesidir.
Max Lybbert

1
Hayır, konseptte eşdeğer değiller. Kavramda bir miktar örtüşme var, ama fazla değil. Her ikisi de Liste <T> oluşturmanıza izin verir, ancak bu yaklaşık olarak devam eder. C ++ şablonları çok daha ileri gider.
jalf

5
Tür silme sorununun yalnızca geriye dönük uyumluluktan daha fazlası anlamına geldiğine dikkat etmek önemlidir Map map = new HashMap<String, String>. Bu, eski bir JVM'ye yeni kod dağıtabileceğiniz anlamına gelir ve bayt kodundaki benzerlikler nedeniyle çalışır.
Yuval Adam

1
"Temelde yüceltilmiş bir önişlemci / makro" dediğimi fark edeceksiniz. Her şablon bildirimi daha fazla kod oluşturacağından (Java / C # yerine) bir benzetme oldu.
cletus

4
Şablon kodu kopyala ve yapıştırdan çok farklı. Makro genişleme açısından, er ya da geç, bunun gibi ince böceklerden etkileneceksiniz: womble.decadentplace.org.uk/c++/…
Nemanja Trifunovic

86

C ++ şablonları vardır. Java, C ++ şablonları gibi biraz sorta benzeyen jeneriklere sahiptir, ancak çok, çok farklıdırlar.

Şablonlar, adından da anlaşılacağı gibi, derleyiciye şablon parametrelerini doldurarak tür güvenli kod oluşturmak için kullanabileceği bir (bekle ...) şablonu sağlayarak çalışır.

Jenerikler, anladığım kadarıyla, başka bir şekilde çalışın: tür parametreleri, bunları kullanan kodun tipte güvenli olduğunu doğrulamak için derleyici tarafından kullanılır, ancak sonuçta ortaya çıkan kod hiç tür olmadan oluşturulur.

C ++ şablonlarını gerçekten iyi bir makro sistemi ve Java jeneriklerini otomatik olarak daktilolar oluşturmak için bir araç olarak düşünün .

 


4
Bu oldukça iyi, özlü bir açıklama. Yapmak isteyeceğim bir değişiklik, Java jeneriklerinin, güvenli olduğu garanti edilen daktiloları otomatik olarak oluşturmak için bir araçtır (bazı durumlarda). Bazı açılardan C ++ 'larla ilişkilidirler const. -++ atmadığı constsürece C ++ 'ta bir nesne bir işaretçi ile değiştirilmez const. Benzer şekilde, Java'da genel türler tarafından oluşturulan örtük yayınların, tür parametreleri kodun herhangi bir yerine manuel olarak dökülmediği sürece "güvenli" olacağı garanti edilir.
Laurence Gonsalves

16

C ++ şablonlarının Java jeneriklerinin sahip olmadığı bir diğer özellik uzmanlıktır. Bu, belirli türler için farklı bir uygulamaya sahip olmanızı sağlar. Böylece, örneğin, türlerin geri kalanı için genel bir sürüme sahipken, bir int için oldukça optimize edilmiş bir sürüme sahip olabilirsiniz. Veya işaretçi ve işaretçi olmayan türler için farklı sürümleriniz olabilir. Bir işaretçi teslim edildiğinde kayıttan çıkarılmış nesne üzerinde çalışmak istiyorsanız bu kullanışlı olur.


1
+1 şablonu uzmanlığı, derleme zamanı meta programlaması için inanılmaz derecede önemlidir - kendi içinde bu fark, java jeneriklerini çok daha az güçlü hale getirir
Faisal Vali

13

Java Generics and Collections'ta bu konuyla ilgili harika bir açıklama By Maurice Naftalin, Philip Wadler. Bu kitabı tavsiye ederim. Alıntılamak:

Java'daki jenerikler C ++ şablonlarına benzer. ... Sözdizimi kasıtlı olarak benzer ve anlambilim kasten farklıdır. ... Anlamsal olarak, Java jenerikleri silme ile tanımlanır, burada C ++ şablonları genişleme ile tanımlanır.

Lütfen tüm açıklamayı buradan okuyun .

alternatif metin
(kaynak: oreilly.com )


5

Temel olarak, AFAIK, C ++ şablonları her tür için kodun bir kopyasını oluştururken Java jenerikleri tam olarak aynı kodu kullanır.

Evet, C ++ şablonunun Java genel kavramına eşdeğer olduğunu söyleyebilirsiniz (daha doğru bir şekilde Java jeneriklerinin kavramdaki C ++ ile eşdeğer olduğunu söylemek gerekir)

C ++ 'ın şablon mekanizmasını biliyorsanız, jeneriklerin benzer olduğunu düşünebilirsiniz, ancak benzerlik yüzeyseldir. Jenerikler her uzmanlık için yeni bir sınıf oluşturmaz ve “şablon meta programlamasına” izin vermez.

from: Java Generics


3

Java (ve C #) jenerikleri, basit bir çalışma zamanı türü değiştirme mekanizması gibi görünmektedir.
C ++ şablonları, dili ihtiyaçlarınıza göre değiştirmenin bir yolunu sunan bir derleme zamanı yapısıdır. Aslında derleyicinin bir derleme sırasında yürüttüğü tamamen işlevsel bir dildir.


3

C ++ şablonlarının bir diğer avantajı uzmanlaşmadır.

template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }

Şimdi işaretçileri ile toplamını ararsanız olmayan pointer ile toplamını ararsanız, ikinci yöntem ilk yöntem çağrılır, nesnelerin adı verilecek ve siz ararsanız sumile Specialnesneler, üçüncü çağrılacak. Bunun Java ile mümkün olduğunu düşünmüyorum.


2
Java işaretçileri olmadığı için olabilir .. !! daha iyi bir örnekle açıklayabilir misiniz?
Bhavuk Mathur

2

Tek bir cümleyle özetleyeceğim: şablonlar yeni türler oluşturur, jenerikler mevcut türleri kısıtlar.


2
Açıklamanız çok kısa! Ve konuyu iyi anlayan insanlar için mükemmel bir mantıklı. Ancak henüz anlamayan insanlar için pek yardımcı olmuyor. (SO hakkında soru soran herkesin durumu nedir
Jakub

1

@Keith:

Bu kod aslında yanlış ve birbirinden küçük glitches (dan templateatlanmış, uzmanlaşma sözdizimi farklı görünüyor), kısmi uzmanlaşma değil yalnızca sınıf şablonlara, fonksiyon şablonları üzerinde çalışmaya. Bununla birlikte, kod kısmi şablon uzmanlığı olmadan çalışır, bunun yerine düz eski aşırı yükleme kullanılır:

template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }

2
Bu neden bir yorum değil bir cevap?
Laurence Gonsalves

3
@Laurence: bir kez, çünkü Yığın Taşması üzerine yorumlar uygulanmadan çok önce yayınlandı. Başka bir deyişle, bu sadece bir yorum değil, aynı zamanda şu sorunun da cevabıdır: Java'da yukarıdaki kod gibi bir şey mümkün değildir.
Konrad Rudolph

1

Aşağıdaki cevap, Kodlama Röportaj Çözümlerini Kırma kitabının Bölüm 13'e kadar olduğunu düşünüyorum, ki bence çok iyi.

Java jeneriklerinin uygulanması "tip silme:" fikrinden kaynaklanır: Bu teknik, kaynak kodu Java Sanal Makinesi (JVM) bayt koduna çevrildiğinde parametrelenmiş türleri ortadan kaldırır. Örneğin, aşağıdaki Java koduna sahip olduğunuzu varsayalım:

Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);

Derleme sırasında, bu kod yeniden yazılır:

Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);

Java jeneriklerinin kullanımı yeteneklerimiz hakkında pek bir şey değiştirmedi; işleri biraz daha güzelleştirdi. Bu nedenle Java jeneriklerine bazen "sözdizimsel şeker:" denir.

Bu C ++ 'dan oldukça farklıdır. C ++ 'da, şablonlar esasen yüceltilmiş bir makro kümesidir ve derleyici her tür için şablon kodunun yeni bir kopyasını oluşturur. Bunun kanıtı, MyClass örneğinin statik bir değişkeni MyClass ile paylaşmayacağı gerçeğidir. Ancak, MyClass'ın yedek örnekleri statik bir değişkeni paylaşacaktır.

/*** MyClass.h ***/
 template<class T> class MyClass {
 public:
 static int val;
 MyClass(int v) { val v;}
 };
 /*** MyClass.cpp ***/
 template<typename T>
 int MyClass<T>::bar;

 template class MyClass<Foo>;
 template class MyClass<Bar>;

 /*** main.cpp ***/
 MyClass<Foo> * fool
 MyClass<Foo> * foo2
 MyClass<Bar> * barl
 MyClass<Bar> * bar2

 new MyClass<Foo>(10);
 new MyClass<Foo>(15);
 new MyClass<Bar>(20);
 new MyClass<Bar>(35);
 int fl fool->val; // will equal 15
 int f2 foo2->val; // will equal 15
 int bl barl->val; // will equal 35
 int b2 bar2->val; // will equal 35

Java'da, statik değişkenler, farklı tür parametrelerinden bağımsız olarak MyClass örnekleri arasında paylaşılır.

Java jenerikleri ve C ++ şablonlarının bir dizi başka farkı vardır. Bunlar:

  • C ++ şablonları int gibi ilkel türleri kullanabilir. Java bunun yerine Tamsayı kullanamaz ve kullanmalıdır.
  • Java'da, şablonun tür parametrelerini belirli bir türle kısıtlayabilirsiniz. Örneğin, bir CardDeck uygulamak ve tür parametresinin CardGame'den genişletilmesi gerektiğini belirtmek için jenerikler kullanabilirsiniz.
  • C ++ 'da, type parametresi somutlaştırılabilir, ancak Java bunu desteklemez.
  • Java'da, type parametresi (yani, Sınıfımdaki Foo) statik yöntemler ve değişkenler için kullanılamaz, çünkü bunlar Sınıfım ve Sınıfım arasında paylaşılacaktır. C ++ 'da bu sınıflar farklıdır, bu nedenle type parametresi statik yöntemler ve değişkenler için kullanılabilir.
  • Java'da, MyClass'ın tür parametrelerine bakılmaksızın tüm örnekleri aynı türdedir. Tür parametreleri çalışma zamanında silinir. C ++ 'da, farklı tip parametrelerine sahip örnekler farklı tiplerdir.

0

Şablonlar bir makro sistemden başka bir şey değildir. Sözdizimi şekeri. Gerçek derlemeden önce tamamen genişletilirler (veya en azından derleyiciler durumdaymış gibi davranırlar).

Misal:

Diyelim ki iki işlev istiyoruz. Bir işlev iki dizi (liste, diziler, vektörler, ne olursa olsun) sayıları alır ve iç çarpımlarını döndürür. Başka bir işlev uzunluk alır, bu uzunluktan iki dizi oluşturur, onları ilk işleve geçirir ve sonucunu döndürür. Yakalama, ikinci fonksiyonda bir hata yapabiliriz, böylece bu iki fonksiyon gerçekten aynı uzunlukta değildir. Bu durumda bizi uyarması için derleyiciye ihtiyacımız var. Program çalışırken değil, derlenirken.

Java'da böyle bir şey yapabilirsiniz:

import java.io.*;
interface ScalarProduct<A> {
    public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
    Nil(){}
    public Integer scalarProduct(Nil second) {
        return 0;
    }
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
    public Integer value;
    public A tail;
    Cons(Integer _value, A _tail) {
        value = _value;
        tail = _tail;
    }
    public Integer scalarProduct(Cons<A> second){
        return value * second.value + tail.scalarProduct(second.tail);
    }
}
class _Test{
    public static Integer main(Integer n){
        return _main(n, 0, new Nil(), new Nil());
    }
    public static <A implements ScalarProduct<A>> 
      Integer _main(Integer n, Integer i, A first, A second){
        if (n == 0) {
            return first.scalarProduct(second);
        } else {
            return _main(n-1, i+1, 
                         new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
            //the following line won't compile, it produces an error:
            //return _main(n-1, i+1, first, new Cons<A>(i*i, second));
        }
    }
}
public class Test{
    public static void main(String [] args){
        System.out.print("Enter a number: ");
        try {
            BufferedReader is = 
              new BufferedReader(new InputStreamReader(System.in));
            String line = is.readLine();
            Integer val = Integer.parseInt(line);
            System.out.println(_Test.main(val));
        } catch (NumberFormatException ex) {
            System.err.println("Not a valid number");
        } catch (IOException e) {
            System.err.println("Unexpected IO ERROR");
        }
    }
}

C # 'da neredeyse aynı şeyi yazabilirsiniz. C ++ 'da yeniden yazmaya çalışın ve şablonların sonsuz genişlemesinden şikayet ederek derlenmeyecek.


Tamam, bu 3 yaşında ama yine de cevap veriyorum. Ne demek istediğini anlamıyorum. Java'nın bu yorumlanmış Hat için bir Hata oluşturmasının tüm nedeni, iki A'nın farklı Bağımsız Değişkenlere (A ve Eksileri <A>) sahip olmasını bekleyen bir işlevi çağırmanızdır ve bu gerçekten basittir ve ayrıca herhangi bir jenerik içermediğinde de gerçekleşir. C ++ bunu da yapar. Bunun dışında bu kod bana kanser verdi çünkü gerçekten korkunç. Ancak, yine de böyle C ++, bunu yapmak zorunda tabii ki değişiklikler C ++ Java değil, ama bu C ++ 's Şablonlar bir dezavantajı değil.
clocktown

@clocktown hayır, bunu C ++ ile yapamazsınız. Hiçbir değişiklik buna izin vermez. Ve bu C ++ şablonlarının bir dezavantajı.
MigMit

Kodunuzun ne yapması gerekiyordu - farklı uzunluk hakkında uyarın - yapmaz. Yorum yaptığınız örnekte, yalnızca eşleşmeyen Bağımsız Değişkenler nedeniyle hatalar üretir. C ++ ile de çalışır. C ++ ve Java'da anlamsal olarak eşdeğer ve bu karışıklıktan çok daha iyi olan Kod Yazabilirsiniz.
clocktown

Öyle. Uzunluklar farklı olduğu için bağımsız değişkenler tam olarak eşleşmiyor. Bunu C ++ ile yapamazsınız.
MigMit

0

Burada askanydifference alıntı yapmak istiyorum:

C ++ ve Java arasındaki temel fark, platforma bağımlı olmalarında yatmaktadır. C ++ platforma bağımlı bir dilken, Java platformdan bağımsız bir dildir.

Yukarıdaki ifade, C ++ 'ın gerçek genel türler sağlayabilmesinin nedenidir. Java sıkı denetime sahip olsa da, bu nedenle jeneriklerin C ++ 'ın izin verdiği şekilde kullanılmasına izin vermezler.

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.