Quelles sont les différences entre les types "génériques" en C++ et Java?

Java a des génériques et C++ fournit un modèle de programmation très fort avec template s. Alors, quelle est la différence entre C++ et Java génériques?

135
demandé sur JesseTG 2008-08-31 01:14:27

12 réponses

Il y a une grande différence entre eux. En C++, vous n'avez pas à spécifier une classe ou une interface de type générique. C'est pourquoi vous pouvez créer des fonctions et des classes vraiment génériques, avec la mise en garde d'une dactylographie plus lâche.

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

La méthode ci-dessus ajoute deux objets du même type, et peut être utilisé pour tout type T qui est l'opérateur "+" disponible.

En Java, vous devez spécifier un type si vous voulez appeler des méthodes sur les objets passé, quelque chose comme:

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

dans C++ les fonctions/classes génériques ne peuvent être définies que dans les en-têtes, puisque le compilateur génère différentes fonctions pour différents types (avec lesquels il est invoqué). La compilation est donc plus lente. En Java, la compilation n'a pas de pénalité majeure, mais Java utilise une technique appelée "erasure" où le type générique est effacé à l'exécution, donc à l'exécution, Java appelle ...

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

si générique programmer en Java n'est pas vraiment utile, c'est juste un peu de sucre syntaxique pour aider avec la nouvelle construction foreach.

EDIT: l'opinion ci-dessus sur l'utilité a été écrite par un moi plus jeune. Java generics aide avec type-sécurité bien sûr.

116
répondu Alexandru Nedelcu 2015-05-22 00:52:53

les génériques Java sont massivement différents des modèles C++.

fondamentalement dans les modèles C++ sont fondamentalement un préprocesseur glorifié / macro ensemble ( Note: puisque certaines personnes semblent incapables de comprendre une analogie, Je ne dis pas que le traitement de modèle est une macro). En Java, ils sont essentiellement du sucre syntaxique pour minimiser le casting boilerplate D'objets. Voici une introduction au c++ les modèles vs Java génériques .

pour développer ce point: quand vous utilisez un template C++, vous créez essentiellement une autre copie du code, tout comme si vous utilisiez une macro #define . Cela vous permet de faire des choses comme avoir int paramètres dans les définitions de gabarit qui déterminent les tailles de tableaux et tels.

Java ne fonctionne pas comme ça. En Java, tous les objets de la mesure de de java.lang.Objet ainsi, pré-génériques, vous écririez le code comme ceci:

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

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

  ...
}

parce que tous les types de collection Java ont utilisé Object comme leur type de base pour que vous puissiez y mettre n'importe quoi. Java 5 roule et ajoute des génériques pour que vous puissiez faire des choses comme:

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

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

  ...
}

et C'est tout ce que Java Generics sont: wrappers for casting objects. C'est parce que les génériques Java ne sont pas raffinés. Ils utilisent le type erasure. Cette décision a été prise parce que Java Generics est arrivé si tard dans la pièce où ils ne voulaient pas casser la compatibilité arrière (un Map<String, String> est utilisable chaque fois qu'un Map est demandé). Comparez ceci à .Net/C# où le type erasure n'est pas utilisé, ce qui conduit à toutes sortes de différences (par exemple, vous pouvez utiliser des types primitifs et IEnumerable et IEnumerable<T> n'ont aucune relation l'un avec l'autre).

et une classe utilisant des génériques compilés avec un compilateur Java 5+ est utilisable sur JDK 1.4 (en supposant qu'il n'utilise pas d'autres fonctionnalités ou classes qui nécessitent Java 5+).

C'est pourquoi les génériques Java sont appelés syntactic sugar .

mais cette décision sur la façon de faire des génériques a des effets profonds à tel point que le (superbe) Java Generics FAQ a surgi pour répondre aux nombreuses, nombreuses questions que les gens ont à propos de Java Generics.

les modèles C++ ont un certain nombre de fonctionnalités que les génériques Java ne possèdent pas:

  • Utilisation des arguments de type primitive.

    par exemple:

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

    Java ne permet pas l'utilisation d'arguments de type primitif dans generics.

  • utilisation de arguments de type par défaut , ce qui est une caractéristique qui me manque en Java, mais il y a des raisons de compatibilité ascendante pour cela;

  • Java permet de délimitation des arguments.

par exemple:

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

il faut vraiment souligner que les invocations de template avec des arguments différents sont vraiment des types différents. Ils ne partagent même pas de membres statiques. En Java, ce n'est pas le cas.

mis à part les différences avec les génériques, pour l'exhaustivité, voici un comparaison de base de C++ et Java (et un autre ).

et je peux aussi suggérer penser en Java . En tant que programmeur C++, Un grand nombre de concepts comme les objets seront déjà de seconde nature, mais il y a des différences subtiles, il peut donc être utile d'avoir un texte d'introduction même si vous avez des parties tronquées.

beaucoup de ce que vous apprendrez en apprenant Java est toutes les bibliothèques (à la fois standard--ce qui vient dans le JDK--et non standard, qui comprend les choses couramment utilisées comme le Printemps). La syntaxe Java est plus verbeuse que la syntaxe C++ et n'a pas beaucoup de fonctionnalités C++ (par exemple la surcharge de l'opérateur, l'héritage multiple, le mécanisme destructeur, etc.) mais cela ne fait pas à proprement parler un sous-ensemble de C++ non plus.

115
répondu cletus 2016-01-31 01:38:02

C++ a des gabarits. Java a des génériques, qui ressemblent un peu à des modèles C++, mais ils sont très, très différents.

Les modèles

fonctionnent, comme leur nom l'indique, en fournissant au compilateur un (wait for it...) template qu'il peut utiliser pour générer du code type-safe en remplissant les paramètres du template.

les Génériques, comme je les comprends, travailler dans l'autre sens: le type de paramètres sont utilisés par le compilateur pour vérifier que le code à l'aide de est de type sécurisé, mais le code est généré sans types.

pense aux modèles C++ comme un vraiment bon macro-système, et Java generics comme un outil pour générer automatiquement des typographies.

73
répondu Shog9 2014-04-03 19:26:45

une autre caractéristique des modèles C++ que les génériques Java ne possèdent pas est la spécialisation. Cela vous permet d'avoir une implémentation différente pour des types spécifiques. Ainsi, vous pouvez, par exemple, pour avoir une version optimisée pour un int , tout en ayant une version générique pour le reste des types. Ou vous pouvez avoir différentes versions pour les types de pointeur et non-pointeur. Cela est pratique si vous voulez opérer sur l'objet déréférencé lorsqu'on vous donne un pointeur.

15
répondu KeithB 2009-01-31 18:23:25

Il ya une grande explication de ce sujet dans Java Génériques et Collections Par Maurice Naftalin, Philip Wadler. Je recommande fortement ce livre. Pour citer:

Génériques En Java ressemblent à des modèles en C.++ ... La syntaxe est délibérément similaire et la sémantique sont délibérément différent. ... Sur le plan sémantique, les génériques Java sont: défini par l'effacement, lorsque, comme en C++ les modèles sont définis par l'expansion.

s'il vous Plaît lire l'intégralité de l'explication ici .

alt texte http://oreilly.com/catalog/covers/0596527756_cat.gif

13
répondu Julien Chastang 2009-01-31 18:16:23

fondamentalement, les modèles AFAIK, C++ créent une copie du code pour chaque type, tandis que les génériques Java utilisent exactement le même code.

Oui, vous peut dire que le C++ modèle est équivalent à Java générique concept ( bien que de manière plus appropriée serait de dire que Java génériques sont équivalents à C++ dans le concept )

si vous êtes familier avec le mécanisme de modèle de C++, vous pourriez pense que les génériques sont similaires, mais la similitude est superficiel. Les produits génériques ne génèrent pas de nouvelle classe pour chaque spécialisation, et ils ne permettent pas non plus une "métaprogrammation de modèle"."

de: Java Génériques

3
répondu OscarRyz 2009-01-31 05:32:13

un autre avantage des gabarits C++ est la spéculation.

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

Maintenant, si vous appelez somme avec les pointeurs, la deuxième méthode est appelée, si vous appelez à somme non-pointeur objets de la première méthode sera appelée, et si vous appelez de la somme() avec des objets Spéciaux, la troisième sera appelée. Je ne pense pas que ce soit possible avec Java.

3
répondu KeithB 2014-01-26 11:29:05

les génériques Java (et C#) semblent être un simple mécanisme de substitution de type run-time.

Les modèles C++ sont une construction de compilation qui vous permet de modifier le langage en fonction de vos besoins. Il s'agit en fait d'un langage purement fonctionnel que le compilateur exécute lors d'une compilation.

2
répondu Ferruccio 2008-09-03 12:00:04

je vais le résumer en une seule phrase: les modèles créent de nouveaux types, les génériques restreignent les types existants.

2
répondu user207421 2016-01-31 02:43:28

@Keith:

ce code est en fait erroné et à part les petits problèmes ( template omis, la syntaxe de spécialisation regarde différemment), la spécialisation partielle ne travail sur les modèles de fonction, seulement sur les modèles de classe. Le code fonctionnerait cependant sans spécialisation partielle du modèle, au lieu d'utiliser une simple vieille surcharge:

template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
0
répondu Konrad Rudolph 2008-09-03 15:50:35
Les modèles

ne sont rien d'autre qu'un macro-système. La syntaxe de sucre. Ils sont entièrement développés avant la compilation proprement dite (ou, du moins, les compilateurs se comportent comme si c'était le cas).

exemple:

disons que nous voulons deux fonctions. Une fonction prend deux séquences (list, arrays, vectors, whatever goes) de nombres, et renvoie leur produit intérieur. Une autre fonction prend une longueur, génère deux séquences de longueur, les passer à la première fonction, et le renvoie du résultat. Le hic est que nous pourrions faire une erreur dans la seconde fonction, de sorte que ces deux fonctions ne sont pas vraiment de la même longueur. Nous avons besoin du compilateur pour nous avertir dans ce cas. Pas quand le programme est en cours d'exécution, mais quand il est en train de compiler.

en Java vous pouvez faire quelque chose comme ceci:

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

dans C# vous pouvez écrire presque la même chose. Essayer de le réécrire en C++, et il ne compilera pas, se plaindre de l'expansion infinie de modèle.

0
répondu MigMit 2013-08-24 16:12:37

La réponse ci-dessous est tirée du livre fendre Le Codage Interview Solutions pour le Chapitre 13, qui je pense est très bon.

l'implémentation de Java generics est enracinée dans une idée de" type erasure:'cette technique élimine les types paramétrés lorsque le code source est traduit en bytecode Java Virtual Machine (JVM). Par exemple, supposons que vous ayez le code Java ci-dessous:

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

pendant la compilation, ce code est réécrit en:

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

L'utilisation de Java generics n'a pas vraiment changé nos capacités; elle a juste rendu les choses un peu plus jolies. Pour cette raison, les génériques Java sont parfois appelés"syntactic sugar:'.

c'est très différent de C++. En C++, Les templates sont essentiellement un ensemble de macros glorifiés, avec le compilateur créant une nouvelle copie du code de template pour chaque type. La preuve de cela est dans le fait qu'une instance de MyClass ne partagera pas de variable statique avec MyClass. Les instances de remorquage de MyClass, cependant, partageront une variable statique.

/*** 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

en Java, les variables statiques sont partagées entre les instances de MyClass, indépendamment des différents paramètres de type.

les modèles génériques Java et C ++ présentent un certain nombre d'autres différences. Il s'agit notamment de:

  • modèles C++ pouvez utiliser les types primitifs, comme int. Java ne peut pas et devoir utilisez plutôt entier.
  • en Java, vous pouvez restreindre les paramètres de type du modèle à un certain type. Par exemple, vous pourriez utiliser des génériques pour mettre en œuvre un CardDeck et spécifier que le paramètre type doit s'étendre de Jeu de cartes.
  • en C++, le paramètre type peut être instancié, alors que Java ne l'est pas. en charge cette.
  • en Java, le paramètre type (i.e., le Foo dans MyClass) ne peut pas être utilisé pour les méthodes statiques et variables, puisque celles-ci seraient partagées entre MyClass et MyClass. En C++, ces classes sont différentes, de sorte que le paramètre type peut être utilisé pour les méthodes et variables statiques.
  • en Java, toutes les instances de MyClass, indépendamment de leurs paramètres de type, sont du même type. Les paramètres de type sont effacés à l'exécution. En C++, les instances avec des paramètres de type différents sont des types différents.
0
répondu Jaycee 2018-07-01 15:59:08