IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SL & STL C++ Discussion :

Hériter des classes de la STL ?


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut Hériter des classes de la STL ?
    Suite à une discussion (lien), je me suis rendu compte que les classes de la STL ne déclarent pas leur destructeur virtuel.
    Or, j'hérite de certaines de ces classes dans mon code (pour enrichir la liste des fonctions membres, ou tout simplement pour faciliter l'écriture) exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template<class T>
    class range : public std::pair<T, T>
    {
    public :
        range() {}
        range(const std::pair<T, T>& pair) : std::pair<T, T>(pair) {}
    };
    En faisant des recherches, j'ai pu lire ça et là que c'était une mauvaise pratique, et j'aimerai savoir pourquoi.
    Au final, le fait que le destructeur ne soit pas virtuel n'est pas grave, puisque dans tous les cas le destructeur de la classe dérivée ne fait strictement rien. Celui de std::pair<> sera toujours appelé, quoi qu'il arrive, donc pas de problème de ce côté là.
    Est-ce une question de performances ?

    Je sais que je peux utiliser une classe du style (c'est ce que je faisais au début) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<class T>
    class range
    {
    public :
     
        // Et là... oubligé de tout redéfinir !
     
    private :
     
        std::pair<T, T> mRange;
    };
    Dans le cas de conteneurs complexes (map, ...), c'est beaucoup de code pour pas grand chose, et ça implique plus de conversions/copies...

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par Kalith Voir le message
    Suite à une discussion (lien), je me suis rendu compte que les classes de la STL ne déclarent pas leur destructeur virtuel.
    Or, j'hérite de certaines de ces classes dans mon code (pour enrichir la liste des fonctions membres, ou tout simplement pour faciliter l'écriture) exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template<class T>
    class range : public std::pair<T, T>
    {
    public :
        range() {}
        range(const std::pair<T, T>& pair) : std::pair<T, T>(pair) {}
    };
    En faisant des recherches, j'ai pu lire ça et là que c'était une mauvaise pratique, et j'aimerai savoir pourquoi.
    Parce que le jour où un utilisateur fera :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    pair<T, T> *toto = new range();
    delete toto;
    Il déclenchera un comportement indéfini.

    Un autre argument est que comme les classes en question n'ont aucune fonction virtuelle, on ne voit pas trop ce que tu y gagne à les dériver, plutôt qu'à définir des fonctions libres qui leur ajoutent des fonctionnalités.

    Citation Envoyé par Kalith Voir le message
    Au final, le fait que le destructeur ne soit pas virtuel n'est pas grave, puisque dans tous les cas le destructeur de la classe dérivée ne fait strictement rien.
    Il ne fait pas forcément rien. Il se peut qu'avec certains compilateurs, ça passe, ou bien ça passe souvent, mais rien n'est garanti, et avec d'autres compilateurs, quelque chose va être corrompu.
    Citation Envoyé par Kalith Voir le message
    Celui de std::pair<> sera toujours appelé, quoi qu'il arrive, donc pas de problème de ce côté là.
    Est-ce une question de performances ?

    Je sais que je peux utiliser une classe du style (c'est ce que je faisais au début) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<class T>
    class range
    {
    public :
     
        // Et là... oubligé de tout redéfinir !
     
    private :
     
        std::pair<T, T> mRange;
    };
    Dans ton cas, si ton compilateur les supporte déjà, est-ce qu'un simple typedef templaté ne ferait pas ce que tu souhaites ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Bonsoir,

    J'en profite pour me joindre a la discutions car je me pose la même question ces derniers jours.

    Je comprend bien de risque de faire un delete sur une class STL qui aurait été dérivée.
    Mais qu'en est-il si on sait que notre class fille ne sera jamais utilisé en tant que ça class mère? Pour la simple raison que ceux qui l'utiliseront ne savent même pas de quoi elle hérite.

    Le risque est assez faible, mais il comme il communément admis que c'est mal, j'avais l'habitude d'utiliser la composition, et de redéfinir tout les accèsseurs nécessaire pour faire de ma class un conteneur "STL-like".(mon problème se porte surtout sur des std::list et std::vector). Mais au bout d'un certains nombre de class comme ça, on se rend compte qu'on réécrit tout le temps la même chose, et qu'as cause de ce foutu destructeur non virtuel, le c++ ne tien pas ça promesse de réutilisablilité(je vais me faire frapper c'est sur...).
    J'ai donc trouvé une alternative:
    Définir une class qui a le comportement d'un vector(En en réencapsulant un), et lui donner un destructeur virtuel.
    Grâce a ça, je peu en hériter, et je règle le problème du destructeur virtuel.

    Seulement, il reste un autre soucie: J'utilise l'héritage alors que "conceptuellement" il s'agit bien d'une composition...
    Alors, est-ce que c'est grave docteur? d'utiliser un héritage pour une composition, juste pour économiser une vingtaine de ligne de code??(sur une vingtaine de class faut dire)
    Mon idée personnelle c'est que le nombre de bugs augmentent globalement proportionnellement au nombre de ligne de code, j'y gagne surement plus que j'y perd. Est-ce que je me trompe?

  4. #4
    Membre Expert
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    mmhh personnellement j'ai tendance à me dire que l'héritage reste la relation la plus forte en C++ et que je préfére (tant que ça a un sens et une validité bien sur) passez par de la composition. Quitte à devoir réécrire des méthodes (pas un simple accesseur, car c'est moche comme solution) surtout qu'en général au final on a pas besoin de tout redéfinir et c'est justement là que amha la composition a plus de sens que l'héritage étant donné que avec l'héritage on se trimballera tout.
    Pour ce qui est du facteur lignes de code, bahh si tu dois réécrire plusieurs fois les mêmes classes (même pour des projets différents) un copier coller et rulez non?

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par Nogane Voir le message
    J'en profite pour me joindre a la discutions car je me pose la même question ces derniers jours.

    Je comprend bien de risque de faire un delete sur une class STL qui aurait été dérivée.
    Mais qu'en est-il si on sait que notre class fille ne sera jamais utilisé en tant que ça class mère? Pour la simple raison que ceux qui l'utiliseront ne savent même pas de quoi elle hérite.
    Si tu le sais, alors le code est légal. Mais es-tu certain de si bien le savoir ?
    Citation Envoyé par Nogane Voir le message
    Le risque est assez faible, mais il comme il communément admis que c'est mal, j'avais l'habitude d'utiliser la composition, et de redéfinir tout les accèsseurs nécessaire pour faire de ma class un conteneur "STL-like".(mon problème se porte surtout sur des std::list et std::vector). Mais au bout d'un certains nombre de class comme ça, on se rend compte qu'on réécrit tout le temps la même chose, et qu'as cause de ce foutu destructeur non virtuel, le c++ ne tien pas ça promesse de réutilisablilité(je vais me faire frapper c'est sur...).


    Je n'ai jamais conçu "réutilisable" comme "tout peut se réutiliser, tout le temps". Je conçois "réutilisable" sur deux plans :
    - On peut aisément utiliser des classes dans un contexte autre que celui où elles ont été conçues (et là, c'est plus une bonne interface et l'encapsulation qui jouent)
    - On peut d'ailleurs adapter certains points précis du comportement de ces classes, qui ont été spécialement étudiés avec une attention particulière, l'exercice étant complexe) au nouveau contexte. L'héritage peut être une technique pour y parvenir, mais n'est certainement pas la seule.

    Pour vector, la question est
    "qu'est-ce qu'on peut vouloir particulariser ?". La réponse a été le type des objets contenus, et l'allocation mémoire.
    "Doit-on pouvoir changer ce comportement au runtime ? Doit on vouloir manipuler de manière uniforme des vecteurs particularisés différemment ?" Non (ce qui n'est d'ailleurs pas forcément une bonne réponse pour l'allocation mémoire) => Plutôt que le polymorphisme, des paramètres templates.

    Du coup, il n'y a rien à gérer par polymorphisme dans la classe => Pas de destructeur virtuel.



    Citation Envoyé par Nogane Voir le message
    J'ai donc trouvé une alternative:
    Définir une class qui a le comportement d'un vector(En en réencapsulant un), et lui donner un destructeur virtuel.
    Grâce a ça, je peu en hériter, et je règle le problème du destructeur virtuel.

    Seulement, il reste un autre soucie: J'utilise l'héritage alors que "conceptuellement" il s'agit bien d'une composition...
    Alors, est-ce que c'est grave docteur? d'utiliser un héritage pour une composition, juste pour économiser une vingtaine de ligne de code??(sur une vingtaine de class faut dire)
    Mon idée personnelle c'est que le nombre de bugs augmentent globalement proportionnellement au nombre de ligne de code, j'y gagne surement plus que j'y perd. Est-ce que je me trompe?
    Pour pouvoir y répondre, il faudrait déjà savoir la raison pour laquelle tu dérive de vector ? J'ai eu une fois où deux l'impression d'avoir le cas typique où c'était utile, à chaque fois que je suis revenu dessus plus tard, je me suis rendu compte que non.

    Peux tu donner un peu plus de contexte sur ton cas d'utilisation ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Je ne peut hélas pas trop rentrer dans les détail.

    En gros, il s'agit de class déjà existantes, qui sont par exemple des liste d'images, des liste de chaine de caractère, des listes de coordonnée, des liste de matrice... (il y en as beaucoup)
    A la base, ces class hérite déjà de leur conteneur.
    Le problème c'est que:
    - Le conteur en question n'est pas template. Il utilise des void*, et nous force a faire des cast de barbare.
    - Difficile de gérer proprement la mémoire avec des pointeur nu (void en plus)
    - Le parcoure est "casse-gueule". (et pas moyen de faire un BOOST_FOREACH)
    - etc...

    Je tente donc de transformer ces conteneur en conteneur "STL-like", n'utilisant pas de pointeurs, ou utilisant des shared_ptr.

    Quand c'est possible, je fait un simple typedef. Mais souvent il y a des méthode supplémentaire spécifique au type contenue.
    Par exemple une liste de points peut contenir des valeur supplémentaire comme la plus petite valeurs en x et en y(c'est une exemple parmi des milliers)
    Dans ce cas je suis obligé de créer une nouvelle class, d'y déclarer un vector, et de redéfinir les iterator, les begin, les end, front, back, []...
    Ou d'en hériter...

    Sur les 4 premières j'ai fait la méthode propre... après j'ai fait la méthode facile... mais j'ai des remords^^

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Ok, prenons l'exemple d'un vecteur qui donne le min de son contenu.

    Première possibilité, il recalcule l'info à chaque fois.

    Dans ce cas, quel est l'intérêt d'hériter ? Sans ça, on peut écrire :
    min(monVector);
    Alors que l'héritage permettra juste d'écrire monVector.min();
    La différence d'écriture justifie-elle l'héritage (sans compter que l'écriture en fonction libre a pas mal de sens, et est peut-être plus générique).


    On va supposer ensuite qu'il met cette info en cache pour aller plus vite, ce qui pourrait justifier qu'on veuille ajouter des données au vector de base, par l'héritage. Mais dans ce cas, il faut aussi que par exemple push_back invalide ou recalcule ce minimum. Or push_back n'est pas virtuel. L'héritage ne nous aide donc pas trop dans cette situation.

    Finalement, j'ai l'impression que ce qui te faudrait, c'est soit une fonction libre (cas 1), soit si la situation réelle est plus complexe un truc comme ce qu'à mis en place boost pour les itérateurs (iterator_facade). En gros, ils ont localisé le coeur fonctionnel d'un itérateur, qui ne contient qu'un nombre très réduit de fonctions, et à partir de ce coeur, un template génère toutes les autres fonctions nécessaire pour avoir un itérateur 100% certifié. Par exemple, tu fournis le begin non const, et il génère la version const automatiquement...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Parce que le jour où un utilisateur fera :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    pair<T, T> *toto = new range();
    delete toto;
    Il déclenchera un comportement indéfini.
    He bien en tout cas, chez moi ça fait exactement ce à quoi je m'attendrais : ça n'appelle que le destructeur de std::pair. Mais si ça n'est pas écrit dans le standard...

    Citation Envoyé par JolyLoic Voir le message
    Un autre argument est que comme les classes en question n'ont aucune fonction virtuelle, on ne voit pas trop ce que tu y gagne à les dériver, plutôt qu'à définir des fonctions libres qui leur ajoutent des fonctionnalités.
    Question de goûts Je n'aime pas les fonctions libres en général.
    L'avantage, c'est que je peux renommer les fonctions membres, exemple le "vector::empty" que je trouve assez mal nommé ("is_empty" aurait été bien moins ambigu, combien de fois je me suis retrouvé à appeler "vector::empty" au lieu de "vector::clear" ).
    Je peux aussi appliquer mes conventions de nommage à ces fonctions, pour avoir un code plus homogène et plus "propre".

    Et si j'utilise l'héritage plutôt que la composition, c'est parce que la version héritée créé moins de conversions, dans un cas comme celui-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Fonction d'une bibliothèque quelconque :
    std::pair<int, int> MyFunction(const std::pair<int, int>& mPair)
    {
        return mPair;
    }
     
    int main()
    {
        range<int> mRange;
     
        mRange = MyFunction(mRange);
     
        return 0;
    }
    ... j'ai testé chez moi, et je vois que, si j'utilise une solution à base de composition, j'ai besoin d'appeler 1 constructeur de plus que si je passe par l'héritage.

    Mais si le prix à payer pour tout ça est un comportement indéfini...
    Alors je pense qu'il ne me reste plus qu'à ré-écrire tout en composition

    Citation Envoyé par JolyLoic Voir le message
    Dans ton cas, si ton compilateur les supporte déjà, est-ce qu'un simple typedef templaté ne ferait pas ce que tu souhaites ?
    Certainement ! Mais la dernière version de GCC fournie par MinGW est assez ancienne et ne supporte pas les typedef template

  9. #9
    Membre Expert
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    Tu peux faire du template rebinding pour contourner ce problème. (y'a un article ici me semble dessus).



    BTW : la dernière version de mingw utilise gcc 4.4.0 et donc tu peux faire joujou avec des features du C++0x

  10. #10
    Membre chevronné

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par défaut
    Citation Envoyé par Goten Voir le message
    BTW : la dernière version de mingw utilise gcc 4.4.0 et donc tu peux faire joujou avec des features du C++0x
    Oh ! En voilà une bonne nouvelle ! Merci beaucoup

  11. #11
    Alp
    Alp est déconnecté
    Expert confirmé

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Par défaut
    Citation Envoyé par Goten Voir le message
    Tu peux faire du template rebinding pour contourner ce problème. (y'a un article ici me semble dessus).
    http://alp.developpez.com/tutoriels/templaterebinding/
    (ça m'en bouche un coin que des gens le lisent et s'en rappellent :)

    Sinon, pour la question principale de ce thread : si la classe de base n'a pas de destructeur virtuel, alors pas la peine d'hériter publiquement, qui plus est lorsque ça n'a pas de sens (cf les nombreux messages & topics sur le Liskov Substitution Principle, entre autres).

    Pour ce genre de choses, tu as héritage protégé/privé ou composition (on préfère souvent utiliser cette dernière).

  12. #12
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par Kalith Voir le message
    He bien en tout cas, chez moi ça fait exactement ce à quoi je m'attendrais : ça n'appelle que le destructeur de std::pair. Mais si ça n'est pas écrit dans le standard...
    un comportement indéfini veut dire que parfois ça marche ... et parfois ça fera tout autre chose avec peut être des erreurs là où tu ne l'attends pas. C'est pour ça que tu ne peux te baser sur le fait que ça marche avec un compilo donc c'est bon. Tu risques d'avoir des pb le jour où tu changes de compilo ou de version du compilo. Sans compter qu'avec un comportement indéfinie, le même compilo peut avoir des comportements différents suivant le contexte.
    Question de goûts Je n'aime pas les fonctions libres en général.
    As-tu pensé aux namespace ?
    L'avantage, c'est que je peux renommer les fonctions membres [...]
    Je peux aussi appliquer mes conventions de nommage à ces fonctions, pour avoir un code plus homogène et plus "propre".
    Tout ceci montre que tu as besoin au pire d'un héritage privé ou au mieux d'une composition.

  13. #13
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Citation Envoyé par Kalith Voir le message
    He bien en tout cas, chez moi ça fait exactement ce à quoi je m'attendrais : ça n'appelle que le destructeur de std::pair. Mais si ça n'est pas écrit dans le standard...
    Ca n'appelle que le destructeur de pair ? Et si le constructeur de rang fait des appel à new, pas de delete -> fuite mémoire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    #include <iostream>
    #include <cstdlib>
     
    struct A
    {
    	~A() { std::cout << "Dest A"; }
    };
     
    struct B : A
    {
    	int* p;
    	B() : p(new int(0)) { std::cout << "Const B"; }
    	~B() { delete p; std::cout << "Dest B"; }
    };
     
    int main ()
    {
    	A* toto = new B();
    	delete toto;
     
    	system("PAUSE");
    	return 0;
    }
    Un héritage privée avec un passage en public des fonction commune à pair et à ta nouvelle classe ne serait pas plus "propre" ?

    Pour l'exemple de la fonction min, je pense aussi qu'il est plus logique de la mettre en fonction libre, ca permet de templatiser la fonction libre pour travailler sur un conteneur quelconque (et pourquoi pas un foncteur pour définir la relation d'ordre).

Discussions similaires

  1. Réponses: 1
    Dernier message: 24/08/2011, 01h01
  2. Réponses: 7
    Dernier message: 01/01/2010, 08h31
  3. Créer les get et set des classes
    Par cameleon2002 dans le forum JBuilder
    Réponses: 3
    Dernier message: 17/09/2003, 21h03
  4. specifier les chemins des .class
    Par draken dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 29/07/2003, 09h35
  5. Inserer des classes java existantes
    Par 2000 dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 20/03/2003, 12h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo