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

C++ Discussion :

déclaration des objet dynamiquement


Sujet :

C++

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    2 051
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 2 051
    Points : 877
    Points
    877
    Par défaut déclaration des objet dynamiquement
    Salut tous,

    ça fait un petit moment que je suis perdu avec un truc :

    => à quoi sert l'allocation dynamique de mémoire pour objet ??? (plutot je devrais dire un pointeur sur objet)

    J'ai bien compris à quoi sert un pointeur ou une référence mais je n'ai pas du tout compris à quoi sert un pointeur alloué dynamiquemeent.


    d'après ce que j'ai compris le fait d'allouer dynamiquement la mémoire (avec New) permet d'avoir un objet qui a une portée plus grande (les termes ne sont pas exactes je pense, en gros ce que je veux dire c'est que si je déclare un objet dynamiquement dans une fonction alors à la fin de la fonction il ne sera pas supprimé contrairement au cas classique).

    => mais ce que je ne comprends pas c'est quand es ce que l'on a besoin d'utiliser ceci ? moi j'utilise toujours des pointeurs classiquement sans allocations dynamique et je n'ai jamais eu de problèmes, du coup je ne comprends pas à quoi sert l'allocation dynamique

    => pouvez vous me donner un exemple qui mettrait en avant concrètement l'avantage de l'allocation dynamique car je ne n'en voit aucun...

    je vous remercie d'avance pour votre aide

    ps: je suis débutant donc s'il vous plait pas de terme trop techniques car je ne les comprendrai surement pas

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Bonjour,

    Citation Envoyé par 21did21 Voir le message
    => mais ce que je ne comprends pas c'est quand es ce que l'on a besoin d'utiliser ceci ? moi j'utilise toujours des pointeurs classiquement sans allocations dynamique et je n'ai jamais eu de problèmes, du coup je ne comprends pas à quoi sert l'allocation dynamique
    Un cas d'école : tu veux faire une liste chaînée pour stocker des éléments qui arrivent au fur et à mesure (par exemple : saisie par l'utilisateur, lecture depuis un fichier, réception par le réseau, etc.). Comment fais-tu pour déclarer de nouvelles entrées sans perdre les autres et les stocker durablement dans ta liste ?

  3. #3
    Membre éprouvé

    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 533
    Points : 1 086
    Points
    1 086
    Par défaut
    Attention au vocabulaire : ton pointeur (ex: char* ptr) est bien alloué statiquement. C'est l'adresse mémoire qu'il contient, qui désigne un objet alloué dynamiquement.

    A quoi sert l'alloc dynamique ?
    Imagine une fonction qui recopie localement une chaîne de caractères dont la taille n'est pas connue à la compilation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void analyser(char* pChaine, int taille)
    {
      char* pChaineLocale = new char[taille];
      std::strcpy (pChaineLocale, pChaine);
     
      // ... utilisation de pChaineLocale ...
     
      delete [] pChaineLocale;  
    }
    En alloc statique, il serait impossible de connaitre à l'avance la taille de pChaine, donc impossible d'en créer une copie locale sur laquelle travailler.

    Bien sûr, c'est juste un exemple.
    Une meilleure solution serait d'utiliser std::string (celle-ci utilisant, en interne, des alloc dynamiques pour pouvoir se (re)dimensionner).

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    En effet, la règle de base, lorsque tu déclares une variable, l'objet qu'elle représente est détruit au moment où on quitte la protée dans laquelle la dite variable est déclarée.

    • Cela peut prendre plusieurs formes : au moment où l'on passe l'accolade fermante du bloc dans lequel la variable est déclarée ou
    • dans le cas de membre de classes / de structure, au moment où la variable correspondant à l'objet "contenant" est détruite
    Deux exemples simples pour te permettre de comprendre
    Au sein d'une fonction d'abord
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void foo()
    {
        UnType variable1;
        if(/* ...*/)
        {
            UnAutreType variable2;
            //variable1 est disponible ici, de meme que variable2
        } // variable2 est détruite ici
        // variable2 n'est pas disponible ici
        // mais variable1 existe toujours
        /* ..*/
    } // variable1 est détruite ici
    pour les membres de classes ou de structure
    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
     
    /* Soit la structure  */
    struct Structure
    {
        UnType membre1;
        UnAutreType membre2;
    };
    // utilisé dans la fonction bar sous la forme de
    void bar()
    {
        Structure structure1; // structure1.membre1 et structure1.membre2
                             // sont construits en meme temps que structure1
                             // ils existeront tant que structure1 existe
     
        if(/* ... */)
        {
            Structure structure2; // il en va de meme pour structure2 ;)
            /* la règle de la portée est la même quelque soit le type de la variable ;) */
        } //structure2 est détruite, et, avec elle, tous ses membres le sont aussi
       /* ...*/
       //structure1 est toujours accessible, ainsi que ses membres ;-) 
    } // structure1 est détruite, ainsi que ses membres ;-)
    Il y a une première exception à la règle, c'est lorsque la variable est renvoyée (sous forme de valeur) par la fonction.

    Sa durée de vie correspond alors à celle de la variable qui récupère le retour de fonction.

    Toujours le meme exemple pour te permettre de comprendre:
    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
    // soit la fonction 
    UnType foo()
    {
        UnType variable;
        /* ... */
        return variable;
    } // la variable n'est pas détruite tout de suite
    // utilisée par la fonction
     
    void bar()
    {
        UnType variable1 = foo(); // la durée de vie de la variable déclarée dans foo
                                  // s'étend maintenant à celle de variable1
        if(/*...*/)
        {
     
            UnType variable2 = foo(); //et il en va de même ici
            /* ...*/
        } // variable2 est détruite ici
        // variable1 est toujours accessible
    } // variable1 est détruite ici
    Jusque là, tout va bien dans le meilleur des mondes, et le recours aux pointeurs ne se justifie pour ainsi dire pas (car, s'il faut transmettre la variable en argument d'une fonction, l'idéal est de la transmettre sous forme de référence, éventuellement constante).

    Un premier problème va survenir lorsqu'on va vouloir assurer une "unicité identitaire" à une variable.

    En terme technique, on parlerait plutot de variable (ou plutot de classe ou de structure ) ayant sémantique d'entité (par opposition à une classe ou une structure ayant sémantique de valeur).

    Une classe (ou une structure, car il n'y a pas des masses de différences entre les deux en C++ )ayant sémantique d'entité est une classe dont chaque instance est strictement unique, même si, d'aventure, il devait arriver d'avoir deux instances ayant les même caractéristiques.

    Les exemples classiques d'une classe ayant sémantique d'entité sont les classes Personne (chaque personne est rigoureusement unique, meme si elle a un sosie ou un jumeau), Voiture (Même si tu trouve une voiture identique en tout point à la tienne, ce ne sera vraiment la tienne que... si c'est vraiment la tienne), CompteBanquaire, et bien d'autres encore.

    Généralement, toute modification que nous pourrions apportée sur une variable dont le type a sémantique d'entité s'effectuera toujours sur le même objet, sans jamais essayer de créer un nouvel objet

    A contrario, les classes (ou les structures) ayant sémantique de valeur peuvent exister "physiquement" plusieurs fois en mémoire.

    C'est le cas pour l'ensemble des nombres, les chaines de caractères, p les coordonnées, les couleurs, bref, tout ce qui peut, d'une manière ou d'une autre, être considéré comme représentant une unité donnée

    Le propre d'une classe (ou d'une structure) ayant sémantique de valeur, c'est que, si on la modifie en quoi que ce soit, nous obtiendront un objet "physiquement" (dans le sens où il prend une adresse mémoire différente) différent de l'objet d'origine.

    Mais, revenons en à nos moutons, et au problème que l'on peut éprouver avec les classes ayant sémantique de valeur

    Tu l'auras compris, une classe ayant sémantique de valeur ne peut, par essence même, pas être copiée (ou, du moins, elle ne devrait pas pouvoir l'être )

    Or, l'insertion dans une collection d'objets risque (d'essayer ) de provoquer une copie de l'objet, sauf... si on place dans la collection non pas l'objet en lui-même mais... une variable qui contient l'adresse de la mémoire à laquelle se trouve le dit objet (autrement dit : un pointeur ) .

    Il faut savoir qu'un pointeur n'est jamais qu'une variable numérique un peu particulière en cela qu'elle représente l'adresse de la mémoire à laquelle on trouvera réellement la variable (ou l'objet).

    Le pointeur en lui-même a donc bel et bien une sémantique de valeur, et peut parfaitement être copié

    Seulement, le fait de travailler avec un pointeur ne résout que la moitié du problème, car il faut s'assurer que l'objet pointé par le pointeur ne sera pas détruit de manière inopportune

    Pour te permettre de comprendre, toujours, imaginons un code proche de
    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
    void foo()
    {
        std::vector<UnType *> tab; // on va stocker des pointeurs, ce qui évitera la copie des objets stockés
     
        for(int i = 0; i< 10; ++i)
        {
            UnType temp; // CRAC
            /* ...*/
            UnType * ptr = & temp; // on prend l'adresse de temp 
            tab.push_back(ptr);  //et on insère l'adresse de temp dans le tableau
     
        } // CRAC BIS : temp est détruit, vu qu'on quitte la portée dans laquelle la variable est déclarée
          // le pointeur que l'on vient de placer dans le tableau pointe... sur une adresse invalide
          // (comprend : sur une adresse à laquelle on ne trouve plus forcément un objet de type UnType)
       /* ... */
       for(size_t i = 0;i < tab.size();++i) // idéalement, on utiliserait des itérateurs, mais restons simples ;)
       {
           tab[i]->unTrucQuelconque(); // BOUM... : le pointeur est "dans la panade"
                                       // Erreur de segmentation assurée
       }
    }
    Pour éviter ce genre de problème, il faut indiquer au compilateur que tu souhaites prendre la responsabilité de la durée de vie des objets que l'on va insérer dans le tableau.

    Cela se fait avec le fameux mot clé new .

    Voici le code tel qu'il devrait être
    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
    void foo()
    {
        std::vector<UnType *> tab; // on va stocker des pointeurs, ce qui évitera la copie des objets stockés
     
        for(int i = 0; i< 10; ++i)
        {
            UnType * ptr = new Untype; // On prend la responsabilité de la durée de vie de l'objet
                               // (il est créé sur le tas, et non sur la pile ;)
            /* ...*/
            tab.push_back(ptr);  //et on insère l'adresse de temp dans le tableau
     
        } // ptr est détruit, mais on s'en fout, il a été copié dans tab, et, surtout, l'objet pointé continue à exister ;)
        /* ... */
        for(size_t i = 0;i < tab.size();++i) // idéalement, on utiliserait des itérateurs, mais restons simples ;)
        {
           tab[i]->unTrucQuelconque(); // Pas de problème : l'objet qui se trouve à l'adresse représentée par le pointeur existe toujours
        }
        /* ...*/
        for(size_t i = 0; i< tab.size(); ++i)
        {
            delete tab[i]; // n'oublions quand meme pas de libérer la mémoire :)
        }
    }
    Un autre problème auquel on peut être confronté est du au fait que, classiquement, les classes (et les structures) ayant sémantique d'entité sont des candidates idéales au fait de servir de classe de base.

    Comprenons nous : il peut parfaitement exister des classes ayant sémantique d'entité qui n'ont absolument pas vocation à être héritées, mais, dés qu'une classe va servir de classe de base à une autre, les deux classes (aussi bien la classe héritée que la classe dérivée) auront fatalement sémantique d'entité

    Et là, les choses se compliquent encore un peu

    Car, qui dit héritage dit... polymorphisme et, qui dit polymorphisme dit que pour en profiter, nous devront travailler sur avec des pointeurs ou avec des références, du moins, tant que l'on souhaite travailler avec des objets passant pour "etre du type de la classe de base".

    Alors, imaginons une hiérarchie de classes proche de
    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
    class Base
    {
        public:
            Base(){}
            virtual ~Base();
            /* ...*/
    };
    class Derivee1 : public Base
    { 
        /* ...*/
    };
    class Derivee2 : public Base
    {
        /*...*/
    };
    et imaginons (car cela se fait souvent ) que nous voulions éviter à l'utilisateur de savoir que Derivee1 ou Derivee2 existe.

    Nous allons fournir une fonction (voire une fonction membre de classe, cf le patron de conception Factory) qui décidera, en fonction d'arguments qui lui sont passés, de construire au choix un objet de type Base, ou un objet de type Derivee1 ou un objet de type Derivee2, mais qui renverra le résultat en le faisant passer comme... un objet de type Base.

    On ne peut pas se permettre de renvoyer l'objet créé par valeur, car on perdrait la possibilité de profiter du polymorphisme (cf plus haut).

    On ne peut pas non plus renvoyer l'objet créé par référence, car nous renverrions une référence sur un objet temporaire (à la fonction) qui serait détruit au moment dur retour de fonction, comme l'indique l'exemple qui suit:
    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
    Base & createFromParameterBAD(/* ... */) // on renvoie une référence, nous devrions pouvoir
                                // profiter du polymorphisme, mais...
    {
        if( condition )
        {
            Base temp; // temp est un objet temporaire
            /* ...*/
            retrun temp;
        } // qui est détruit ici
        if( autre condition )
        {
            Derivee1 deriv1; // et il en va pour tous pareil
            return deriv1;
        }
        if( troisieme condition )
        {
            Derivee2 deriv2; 
            return deriv2;
        }
    }
    On est donc obligé de faire en sorte que la durée de vie de l'objet qui sera créé soit supérieur à la portée dans laquelle il est créé.

    Et, pour ce faire, il n'y a qu'un seul moyen : utiliser les pointeurs etl'allocation dynamique:
    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
     
    Base * createFromParameterGOOD(/* ... */) // on renvoie un pointeur pour profiter du polymorphisme
    {
        if( condition )
        {
            Base  * tempptr = new Base; //la durée de vie de l'objet pointé par tempptr ne dépend plus de la portée
            /* ...*/
            retrun tempptr;
        } // qui est détruit ici
        if( autre condition )
        {
            Derivee1 *  deriv1ptr = new Derivee1; // et il en va pour tous pareil
            return deriv1ptr;
        }
        if( troisieme condition )
        {
            Derivee2 * deriv2ptr = new Derivee2; 
            return deriv2;
        }
    }
    et elle pourrait etre utilisée sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void foo()
    {
        Base * ptr = createFromParameterGOOD(/* ... */);
        /* on peut utiliser toutes les fonctions membre de Base ici, en étant
         * sur que les comportements polymorphes seront adaptés au
         * type réel (Base, Derivee1 ou Derivee2, en fonction des paramètres
         * passés à createFromParameterGOOD
         */
        /* ...*/
        delete ptr; // on n'oublie quand meme pas de libérer la mémoire
                    // allouée à l'objet quand on n'en a plus besoin ;)
    }
    J'espère que cette (longue) explication t'auras donné toutes les infos utiles pour comprendre
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    En attente de confirmation mail

    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 : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Il y a une première exception à la règle, c'est lorsque la variable est renvoyée (sous forme de valeur) par la fonction.

    Sa durée de vie correspond alors à celle de la variable qui récupère le retour de fonction.
    C'est pas vraiment un exception, le compilateur a le droit (d'un point de vue norme) de le faire, mais rien ne l'y oblige. Cependant je suis bien conscient qu'ils doivent souvent (*) le faire en pratique, mais, AMA, il ne faut pas le présenter comme une vérité absolue.

    (*) J'ai déjà observé certains cas où il ne les faisait pas toute, notament en expérimentant les solutions proposés par Dave.A sur C++Next pour optimiser les opérations matricielles en C++03 (sans expression template).

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    2 051
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 2 051
    Points : 877
    Points
    877
    Par défaut
    merci beaucoup tous pour votre aide et merci beaucoup Koala pour ra réponse très complete !!!!!!

    Citation Envoyé par koala01 Voir le message
    En effet, la règle de base, lorsque tu déclares une variable, l'objet qu'elle représente est détruit au moment où on quitte la protée dans laquelle la dite variable est déclarée.
    • Cela peut prendre plusieurs formes : au moment où l'on passe l'accolade fermante du bloc dans lequel la variable est déclarée ou
    • dans le cas de membre de classes / de structure, au moment où la variable correspondant à l'objet "contenant" est détruite
    Deux exemples simples pour te permettre de comprendre
    Au sein d'une fonction d'abord
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void foo()
    } // variable1 est détruite ici
    pour les membres de classes ou de structure
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    /* Soit la structure  */
    } // structure1 est détruite, ainsi que ses membres ;-)
    Il y a une première exception à la règle, c'est lorsque la variable est renvoyée (sous forme de valeur) par la fonction.

    Sa durée de vie correspond alors à celle de la variable qui récupère le retour de fonction.

    Toujours le meme exemple pour te permettre de comprendre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    // soit la fonction 
    } // variable1 est détruite ici
    Jusque là, tout va bien dans le meilleur des mondes, et le recours aux pointeurs ne se justifie pour ainsi dire pas (car, s'il faut transmettre la variable en argument d'une fonction, l'idéal est de la transmettre sous forme de référence, éventuellement constante).
    jusqu'à la j'ai tout compris


    Citation Envoyé par koala01 Voir le message
    Un premier problème va survenir lorsqu'on va vouloir assurer une "unicité identitaire" à une variable.
    En terme technique, on parlerait plutot de variable (ou plutot de classe ou de structure ) ayant sémantique d'entité (par opposition à une classe ou une structure ayant sémantique de valeur).
    Une classe (ou une structure, car il n'y a pas des masses de différences entre les deux en C++ )ayant sémantique d'entité est une classe dont chaque instance est strictement unique, même si, d'aventure, il devait arriver d'avoir deux instances ayant les même caractéristiques.
    Les exemples classiques d'une classe ayant sémantique d'entité sont les classes Personne (chaque personne est rigoureusement unique, meme si elle a un sosie ou un jumeau), Voiture (Même si tu trouve une voiture identique en tout point à la tienne, ce ne sera vraiment la tienne que... si c'est vraiment la tienne), CompteBanquaire, et bien d'autres encore.
    Généralement, toute modification que nous pourrions apportée sur une variable dont le type a sémantique d'entité s'effectuera toujours sur le même objet, sans jamais essayer de créer un nouvel objet
    A contrario, les classes (ou les structures) ayant sémantique de valeur peuvent exister "physiquement" plusieurs fois en mémoire.
    C'est le cas pour l'ensemble des nombres, les chaines de caractères, p les coordonnées, les couleurs, bref, tout ce qui peut, d'une manière ou d'une autre, être considéré comme représentant une unité donnée
    Le propre d'une classe (ou d'une structure) ayant sémantique de valeur, c'est que, si on la modifie en quoi que ce soit, nous obtiendront un objet "physiquement" (dans le sens où il prend une adresse mémoire différente) différent de l'objet d'origine.
    Mais, revenons en à nos moutons, et au problème que l'on peut éprouver avec les classes ayant sémantique de valeur
    Tu l'auras compris, une classe ayant sémantique de valeur ne peut, par essence même, pas être copiée (ou, du moins, elle ne devrait pas pouvoir l'être )
    Or, l'insertion dans une collection d'objets risque (d'essayer ) de provoquer une copie de l'objet, sauf... si on place dans la collection non pas l'objet en lui-même mais... une variable qui contient l'adresse de la mémoire à laquelle se trouve le dit objet (autrement dit : un pointeur ) .
    Il faut savoir qu'un pointeur n'est jamais qu'une variable numérique un peu particulière en cela qu'elle représente l'adresse de la mémoire à laquelle on trouvera réellement la variable (ou l'objet).
    Le pointeur en lui-même a donc bel et bien une sémantique de valeur, et peut parfaitement être copié
    bien que ça soit un peu technique pour moi je pense avoir saisi

    Citation Envoyé par koala01 Voir le message
    Seulement, le fait de travailler avec un pointeur ne résout que la moitié du problème, car il faut s'assurer que l'objet pointé par le pointeur ne sera pas détruit de manière inopportune

    Pour te permettre de comprendre, toujours, imaginons un code proche de
    Pour éviter ce genre de problème, il faut indiquer au compilateur que tu souhaites prendre la responsabilité de la durée de vie des objets que l'on va insérer dans le tableau.
    Cela se fait avec le fameux mot clé new .
    Voici le code tel qu'il devrait être
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void foo()
    {
     
        }
    }
    => super exemple j'ai tout compris

    Citation Envoyé par koala01 Voir le message
    Un autre problème auquel on peut être confronté est du au fait que, classiquement, les classes (et les structures) ayant sémantique d'entité sont des candidates idéales au fait de servir de classe de base.
    Comprenons nous : il peut parfaitement exister des classes ayant sémantique d'entité qui n'ont absolument pas vocation à être héritées, mais, dés qu'une classe va servir de classe de base à une autre, les deux classes (aussi bien la classe héritée que la classe dérivée) auront fatalement sémantique d'entité
    Et là, les choses se compliquent encore un peu
    Car, qui dit héritage dit... polymorphisme et, qui dit polymorphisme dit que pour en profiter, nous devront travailler sur avec des pointeurs ou avec des références, du moins, tant que l'on souhaite travailler avec des objets passant pour "etre du type de la classe de base".
    là par contre je n'ai pas compris car je n'utilise jamais l'héritage (et encore moins le polymorphisme que je ne connais que de nom).
    En tout cas je reviendrai sur ces explications (qui m'ont l'air super encore une fois) si jamais je me mets à utiliser l'héritage ou le polymorphisme

    ==> merci Koala pour ces super explication

  7. #7
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    2 051
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 2 051
    Points : 877
    Points
    877
    Par défaut
    Koala j'ai un soucis par contre sur un truc.

    -> ce que je vais dire ne tiens pas compte du cas où il y a heritage que je n'utilisee jamais (car je ne l'ai pas compris vu que je ne sais pas ce que c'est le polymorphisme)

    j'ai l'impression que on peut toujours se passer des allocations dynamique....

    je m'explique :

    moi quand je code je déclare tous mes objets dans le fichier "main.cpp".
    Ensuite je fais appel aux méthodes de ces divers objets en donnant en argument les autres objets pour que l'on puisse s'en servir et du coup je n'ai jamais utilisé d'allocation dynamique...
    Du coup, hors héritage je ne vois pas quand es ce que c'est indispensable (dans ton exemple j'ai bien compris mais au aurait pu le programmer autrement pour que ça ne soit plus obligatoire).

    Voici un exemple de code typique que je ferai :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    main.cpp
    {
    MaClass1 objet1;
    MaClass2 objet2;
    MaClass3 objet3;
    MaClass4 objet4;
     
    objet1.calcul(objet2,objet3,objet4)
    objet2.calcul2(objet1,objet3,objet4)
     
    //....
    //j'arrive à la fin du code et mes objets sont tous détruits 
    //mais ce n'est pas grave car j'ai fais tous les calculs dont j'avais besoin.
    }

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    MaClass1::calcul(Maclass2 &objet2,Maclass3 &objet3,Maclass4 &objet4)
    {
    objet2.calcul2(objet3,objet4)
    objet3.calcul(objet2,objet4)
    objet4.calcul(objet2,objet3)
    //là je fais tous mes calculs intermédiaires
    }


    que pensais vous de cette méthode ? est elle bonne ? es ce que l'allocation dynamique viendrait améliorer quelque chose ?

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 214
    Points : 310
    Points
    310
    Par défaut
    Si tu as besoin de 5 objets de classe identique et non de 4, tu fais comment, tu crée un nouveau programme ?

  9. #9
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Tu te rends bien compte que ça devient infernal de gérer la mémoire comme ça, et super redondant.
    Évidemment, dans ton exemple on peut s'en passer.

    Il vient un moment ou on ne peut pas s'en passer, c'est quand la taille d'un tableau dépend d'un élément extérieur au programme. Essaye de ne pas t'en passer en faisant un programme qui :

    1) Demande à l'utilisateur un nombre "n".
    2) Demande à l'utilisateur n nombres.
    3) Affiche ces n nombres.
    4) Affiche une deuxième fois ces n nombres.

    Dans tout les cas, si tu réserves N cases de mémoire, l'utilisateur pourra te "forcer" à en avoir besoin de N+1.

    Ça peut être un utilisateur, une base de donnée, via un socket, ou je ne sais quoi...

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Jérôme_C Voir le message
    Si tu as besoin de 5 objets de classe identique et non de 4, tu fais comment, tu crée un nouveau programme ?
    Il est toujours possible d'utiliser les conteneurs de la STL, meme si, soit dit en passant, ils utilisent en interne l'allocation dynamique de la mémoire

    L'utilisateur de ces classes n'a, en tout état de cause, pas forcément besoin de s'en inquiéter...

    Du moins, tant que ses classes peuvent rester copiables
    Citation Envoyé par Trademark Voir le message
    Tu te rends bien compte que ça devient infernal de gérer la mémoire comme ça, et super redondant.
    Évidemment, dans ton exemple on peut s'en passer.

    Il vient un moment ou on ne peut pas s'en passer, c'est quand la taille d'un tableau dépend d'un élément extérieur au programme. Essaye de ne pas t'en passer en faisant un programme qui :

    1) Demande à l'utilisateur un nombre "n".
    2) Demande à l'utilisateur n nombres.
    3) Affiche ces n nombres.
    4) Affiche une deuxième fois ces n nombres.

    Dans tout les cas, si tu réserves N cases de mémoire, l'utilisateur pourra te "forcer" à en avoir besoin de N+1.
    Encore une fois, tu peux gérer ce genre de situation en utilisant les conteneurs de la STL, comme on n'arrête pas de le conseiller dans le cadre d'un C++ "moderne"

    Mais, bien sur, cela implique que l'on travaille avec des classes ayant sémantique de valeur

    Ça peut être un utilisateur, une base de donnée, via un socket, ou je ne sais quoi...
    Tant que les données reçues ont sémantique de valeur, cela ne pose pas forcément de problème
    Citation Envoyé par 21did21 Voir le message
    Koala j'ai un soucis par contre sur un truc.

    -> ce que je vais dire ne tiens pas compte du cas où il y a heritage que je n'utilisee jamais (car je ne l'ai pas compris vu que je ne sais pas ce que c'est le polymorphisme)
    Le polymorphisme est le pendant de ce qui fait la base même de la programmation orientée objet.

    En deux mots, le concept orienté objet propose de ne plus réfléchir à nos structures en termes de données qui les composent, mais en termes de services qu'elles vont nous rendre, les données dont elles sont composées devenant un "détail d'implémentation" qui permettra à notre objet de rendre les services attendus


    On va donc "catégoriser" nos objets en fonctions de leur "famille", et l'on va régulièrement se dire que "tel type EST-UN type particulier" (par exemple, une voiture EST-UN véhicule, et, à ce titre, la classe Voiture va rendre au minimum tous les services que l'on peut attendre d'un véhicule).

    Le gros principe pour pouvoir avoir ce genre de décision est la subsituabilité : on doit, pour suivre mon exemple, pouvoir transmettre un objet de type Voiture à toute fonction attendant un Vehicule (sous la forme d'un pointeur ou d'une référence) comme paramètre.

    La règle primordiale à respecter dans le cas d'un héritage est le Principe de Substitution de Liskov (Liskov Substution Principle, ou LSP pour les intimes) qui dit que "si Q(x) est une propriété démontrable pour un objet x de type T, alors Q(y) doit etre valide pour tout objet y de type S étant un sous type de T" (il y d'autre traductions qui veulent toutes dire la meme chose )

    Cependant, il n'est pas impossible que deux objets dérivés utilisent une manière différente de rendre un meme service: le comportement "interne" du service a beau être différent, le service rendu reste le même.

    Le comportement interne ne fait que... s'adapter au type réel de l'objet impliqué

    Tu pourrais, par exemple, vouloir créer une classe Loader qui s'occupe de récupérer les données et de créer un certain nombre d'objets en fonction des données qu'elle obtient.

    Mais il est possible d'envisager plusieurs sources de données : depuis un fichier, depuis un serveur réseau, depuis une base de données, depuis des capteurs, ou que sais-je.

    Tu vas donc "spécialiser" (grace à l'héritage) ta classe Loader en autant de classes que tu as de sources de données différentes, chargeant chaque fois les données de manière particulières, mais ayant un résultat final identique et tu pourras transmettre n'importe quelle spécialisation à une fonction qui prend un pointeur ou une référence sur un loader de manière générale en étant sur que le travail sera correctement effectué

    Cela fait un peu plus de deux mots, mais j'espère que cela t'auras permis de comprendre le pourquoi et le comment de l'héritage et du polymorphisme

    j'ai l'impression que on peut toujours se passer des allocations dynamique....
    Je comprend parfaitement cette impression, mais j'ai l'impression que c'est surtout parce que tu ne t'es jamais trouvé dans une situation où la seule solution était effectivement de recourir à l'allocation dynamique...

    Ne t'en fais pas, cela viendra forcément en son temps
    je m'explique :

    moi quand je code je déclare tous mes objets dans le fichier "main.cpp".
    Ensuite je fais appel aux méthodes de ces divers objets en donnant en argument les autres objets pour que l'on puisse s'en servir et du coup je n'ai jamais utilisé d'allocation dynamique...
    Malheureusement, cette manière de travailler est très rarement optimale, ne serait ce que parce qu'elle t'oblige à prévoir dés le départ les objets dont tu auras besoin, ce qui rend très difficile tout évolution ultérieure
    Du coup, hors héritage je ne vois pas quand es ce que c'est indispensable (dans ton exemple j'ai bien compris mais au aurait pu le programmer autrement pour que ça ne soit plus obligatoire).
    Même hors héritage, il est très difficile d'éviter tout recours à l'allocation dynamique.

    Que ce soit parce que l'objet peut etre créé ou non, parce que le type d'un objet a sémantique d'entité (et n'a donc pas vocation à être copié), alors qu'il est destiné à être placé dans une collection quelconque, parce que bien que le type ait sémantique de valeur (et puisse donc être copié), la copie devient trop couteuse en temps ou en ressource mémoire, et bien d'autres cas encore
    Voici un exemple de code typique que je ferai :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    main.cpp
    {
    MaClass1 objet1;
    MaClass2 objet2;
    MaClass3 objet3;
    MaClass4 objet4;
     
    objet1.calcul(objet2,objet3,objet4)
    objet2.calcul2(objet1,objet3,objet4)
     
    //....
    //j'arrive à la fin du code et mes objets sont tous détruits 
    //mais ce n'est pas grave car j'ai fais tous les calculs dont j'avais besoin.
    }

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    MaClass1::calcul(Maclass2 &objet2,Maclass3 &objet3,Maclass4 &objet4)
    {
    objet2.calcul2(objet3,objet4)
    objet3.calcul(objet2,objet4)
    objet4.calcul(objet2,objet3)
    //là je fais tous mes calculs intermédiaires
    }


    que pensais vous de cette méthode ? est elle bonne ?
    Ce n'est pas forcément une mauvaise maniière de travailler, mais elle a ses limites, et elles sont facilement atteintes
    est ce que l'allocation dynamique viendrait améliorer quelque chose ?
    C'est à voir, en fonction des relations qui existent entre tes différentes classes, en fonction des manipulations effectuées et en fonction de l'évolutivité nécessaire à ton projet.

    Tel que tu présente les choses, je dirais que, en l'état, l'allocation dynamique n'apporterait sans doute pas grand chose, mais, avec un peu plus de précision (par exemple : quelles sont les responsabilités respectives des différentes classes Quel genre de comportement est attendu des fonctions calcul ), pourrait très facilement s'avérer plus intéressant de travailler d'une autre manière impliquant l'héritage et le polymorphisme, voire l'allocation dynamique pure et simple

    N'oublies pas qu'il y a un certain nombre de règles à respecter afin de garantir une certaine évolutivité ou d'obtenir quelque chose de facilement maintenable et d'aussi exempt de bug que possible.

    Il s'agit de règles quasiment universelles en programmation telles que :
    • La règle de la responsabilité unique (chaque fonction, chaque classe, ne doit s'occuper que d'une seule chose, mais doit s'en occuper correctement)
    • Le fameux "ne vous répétez pas" : si un même code doit se trouver à plusieurs endroits, il est utile de le factoriser de manière à éviter les erreurs de copies et, surtout, de manière à éviter d'avoir à le modifier en plusieurs endroits si le besoin s'en fait sentir (car, sinon, il arrivera toujours un moment où l'on oubliera de le modifier à un endroit )
    • La loi demeter qui dit que si un objet A utilise un objet B et que l'objet B utilise un objet C, l'objet A ne devrait pas avoir à connaitre l'objet C pour pouvoir manipuler l'objet B
    • et d'autres encore ...
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Encore une fois, tu peux gérer ce genre de situation en utilisant les conteneurs de la STL, comme on n'arrête pas de le conseiller dans le cadre d'un C++ "moderne"
    Mais comme tu dis si bien :

    ils utilisent en interne l'allocation dynamique de la mémoire
    Donc on ne peut pas s'en passer !

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Trademark Voir le message
    Encore une fois, tu peux gérer ce genre de situation en utilisant les conteneurs de la STL, comme on n'arrête pas de le conseiller dans le cadre d'un C++ "moderne"
    Mais comme tu dis si bien :
    ils utilisent en interne l'allocation dynamique de la mémoire
    Donc on ne peut pas s'en passer !
    Attention, il faut ici faire la différence entre ce qui se fait en interne dans une bibliothèque et la manière dont on l'utilise

    La STL manipule effectivement l'allocation dynamique de la mémoire en interne, mais elle le fait de manière totalement opaque pour l'utilisateur!

    Si bien que l'utilisateur peut avoir à ne jamais utiliser le mot clé new, et donc ne jamais utiliser lui-même l'allocation dynamique
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 214
    Points : 310
    Points
    310
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Attention, il faut ici faire la différence entre ce qui se fait en interne dans une bibliothèque et la manière dont on l'utilise

    La STL manipule effectivement l'allocation dynamique de la mémoire en interne, mais elle le fait de manière totalement opaque pour l'utilisateur!

    Si bien que l'utilisateur peut avoir à ne jamais utiliser le mot clé new, et donc ne jamais utiliser lui-même l'allocation dynamique
    Utiliser le mot-clé new, c'est de l'allocation dynamique, mais utiliser une fonction qui encapsule ce new, ce n'est plus de l'allocation dynamique ?

    Même les enfants apprennent ça dès le plus jeune âge, ce n'est pas parce qu'une chose ou une personne n'est plus visible (caché derrière un objet, dans la pièce d'à côté...) qu'elle n'existe plus.


    Certes, on ne gère pas cette allocation dynamique, mais il y a quand même de l'allocation dynamique dans le projet, et n'est-ce pas le sujet finalement ?

  14. #14
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Jérôme_C Voir le message
    Utiliser le mot-clé new, c'est de l'allocation dynamique, mais utiliser une fonction qui encapsule ce new, ce n'est plus de l'allocation dynamique ?
    on pourrait presque dire cela sous cette forme...

    Surtout si tu n'utilises jamais de pointeurs, il n'y aura rien au niveau de ton code à toi (à l'exclusion, donc, de tout ce qui vient d'une bibliothèque externe) qui peuvent laisser penser qu'elle est utilisée

    D'ailleurs, un grep sur new appliqué à l'ensemble du projet peut parfaitement te confirmer cette impression
    Même les enfants apprennent ça dès le plus jeune âge, ce n'est pas parce qu'une chose ou une personne n'est plus visible (caché derrière un objet, dans la pièce d'à côté...) qu'elle n'existe plus.
    Ce n'est pas vraiement ce dont on parle ici...

    Il ne faut pas oublier que la conception informatique va régulièrement à l'encontre de ce que l'on peut apprendre à l'école ( cf le fameux débat sur l'héritage (ou l'absence d'héritage) entre le rectangle et le carré)

    Certes, on ne gère pas cette allocation dynamique, mais il y a quand même de l'allocation dynamique dans le projet,
    Pas dans le projet (un grep sur new appliqué au projet te confirmera que tu n'utilises absolument pas l'allocation dynamique )
    et n'est-ce pas le sujet finalement ?
    Non, le sujet est "dois-je impérativement utiliser new de manière explicite "

    Et, je suis désolé, la réponse est, bel et bien non: tu n'es pas forcément obligé de recourir de manière explicite à l'opérateur new.

    Le fait que la bibliothèque que tu utilises en fasse usage est totalement "hors scope" car tu peux parfaitement (avec d'autres bibliothèques plus fermées que la STL, par exemple) n'avoir purement et simplement aucune idée de ce que la bibliothèque fait en interne, et que c'est d'ailleurs le but même d'une bibliothèque
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  15. #15
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    2 051
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 2 051
    Points : 877
    Points
    877
    Par défaut
    bonjour tous et merci pour ces super réponses

    je vais vous donner un exemple de trucs que je souhaite faire et vous expliquer pourquoi mon calcul ressemble à ceci.

    - j'ai une classe par exemple "voiture" avec pour attribut : puissance, cylindrée...
    - une classe "carburant" avec attributs : diesel, essence...
    - et une classe route avec attributs : rugosité, revetement...
    - une classe fluide avec attribut : humité air, temperature...

    Ceci sont toutes mes classes de bases. Ensuite j'ai des classes plus globales :
    - visualisation resultats
    - calculs

    Dans mon code je dois faire des calculs du type calcul de la consommation du vehicule X qui est un objet de la classe "voiture". Une fois le calcul effectué je dois stocker la valeur en attribut de la classe voiture.

    Le soucis est que pour faire mes calcul je dois utilisé les attributs des classes "route", "fluide"....etc

    du coup, voici la manière dont je procède :
    dans mon fichier main.cpp je déclare tout et j'envoi tout ces objets à la classe calcul du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    main
    {
    calcul.calculConsommation(voiture,route,fluide)
    }
    et après la "calculConsommation" fait un calcul qui fait intervenir toutes les classes et stocke le résultat dans l'objet voiture.

    Voici typiquement la manière dont je procéde pour structurer mon code. J'ai conscience que ce n'est pas optimal mais je ne vois pas comment faire autrement car en réalité je n'ai jamais vu d'exemple de programme complet en C++. J'ai toujours vu des petits exemple avec un seul classe et du coup je n'arrive pas à structurer un code où il y aurait plusieurs classes et plusieurs de ses classes s'appelent entre elles

    pourriez vous me dire comment procéderiez vous par exemple dans ce cas ? où connaissez vous un site où on peut trouver des exemples de codes complet en C++ qui me montrer comment organiser un programme

  16. #16
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2008
    Messages : 214
    Points : 310
    Points
    310
    Par défaut
    Non, le sujet est "dois-je impérativement utiliser new de manière explicite "

    Et, je suis désolé, la réponse est, bel et bien non: tu n'es pas forcément obligé de recourir de manière explicite à l'opérateur new.
    Certes, dans ce cas, si utilisation de l'allocation dynamique = utilisation de new, on restreint le débat, et on se met à trouver des cas où le mot-clef new n'est pas obligatoire.

    Mais ce terme allocation dynamique, c'est plus un principe, que simplement un mot-clef associé.


    Le fait que la bibliothèque que tu utilises en fasse usage est totalement "hors scope" car tu peux parfaitement (avec d'autres bibliothèques plus fermées que la STL, par exemple) n'avoir purement et simplement aucune idée de ce que la bibliothèque fait en interne, et que c'est d'ailleurs le but même d'une bibliothèque
    Du coup, je comprend mieux ton point de vue !!

    Le but d'une bibliothèque n'est pas forcément de maintenir dans l'ignorance son utilisateur. Surtout, je pense qu'il faut connaitre un minimum des caractéristiques de la bibliothèque. Si par exemple une bibliothèque permet de calculer 2 + 2, et qu'il y a pléthore d'allocation dynamique, écriture disque, communication réseau et que sais-je encore, est-ce vraiment une bonne bibliothèque pour quelque chose d'aussi simple que 2 + 2 ?

    Je n'utilise jamais l'électricité chez moi, car je ne branche jamais une prise de courant ??






    21did21, ton problème ne m'a pas forcément l'air d'être un problème d'allocation dynamique.

    Quelques remarques :
    - Une classe calcul n'est pas forcément pertinent. S'il n'y a aucune données propre à ta classe, un namespace suffirait pour stocker tes claculs, non ?
    - Si tu fais du C++, autant profiter que les classes et les structures peuvent avoir des fonctions. Ainsi une fonction calculConsommation peut facilement s'envisager comme fonction membre de la classe voiture. Les choix des fonctions dans une classe ou en dehors, ce sont les principes de l'encapsulation qui les définissent (et donc lorsque tu joues avec les propriétés public et privé) : Une fonction a accès au minimum d'information, et elle a le pouvoir minimum pour modifier les données.
    - Une classe peut avoir une autre classe en tant que membre, tu n'es pas obligé d'avoir seulement des types élémentaires. Ainsi, ta classe voiture peut avoir une association avec un carburant particulier.



    L'organisation de ton modèle de données est une chose, mais ce n'est pas en prenant le problème du simple calcul pour un cas particulier que tu verras l'intérêt de l'allocation dynamique.

    En fait, c'est lorsque tu dois généraliser le programme et gérer des carburants différents mais en nombre limité, que plusieurs voitures pourront avoir le même carburant, que de nouvelles voitures peuvent être traitées, que tu dois réfléchir comment est géré en mémoire tous ces éléments. Mais pour juste un calcul, on est d'accord que généralement ça ne sert à rien.

  17. #17
    Membre émérite
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Points : 2 274
    Points
    2 274
    Par défaut
    Salut,

    Citation Envoyé par 21did21 Voir le message
    - j'ai une classe par exemple "voiture" avec pour attribut : puissance, cylindrée...
    En fait, tu te poses des questions de débutant comme tout à chacun qui commence à entrer dans le vif du sujet de ce langage. L'allocation dynamique ne se fait pas sentir encore parce que ton design reste très terre-à-terre. Dans une vision plus ouverte, la cylindrée se rattacherait plutôt à un objet moteur. À côté de cela, tu as le type du moteur qui peut être diesel, essence, bioéthanol, ect, mais encore, le carburant est un autre objet, où on peut avoir le diesel, le sp95, le sp98, le propane, etc., ce qui ne veut pas dire que tu ne peux pas mettre de biocarburant dans la voiture équipée d'un moteur diesel.

    C'est en prenant compte de toutes ces considérations que le besoin de l'héritage et du polymorphisme se fera sentir, et l'allocation dynamique aussi, où dans ta classe dérivée tu vas prévoir leur instanciation, mais ce ne sera pas forcément utilisé. Voilà, sans ça, on risque de se retrouver avec un vélo qui à un moteur, donc à chaque fois ça va t'obliger à revoir et corriger ce que tu as codé précédemment, alors qu'un des bute de c++ c'est de faire des pièces détache, et d'utiliser juste ce qu'on a besoin.

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Jérôme_C Voir le message
    Certes, dans ce cas, si utilisation de l'allocation dynamique = utilisation de new, on restreint le débat, et on se met à trouver des cas où le mot-clef new n'est pas obligatoire.

    Mais ce terme allocation dynamique, c'est plus un principe, que simplement un mot-clef associé.



    Du coup, je comprend mieux ton point de vue !!

    Le but d'une bibliothèque n'est pas forcément de maintenir dans l'ignorance son utilisateur. Surtout, je pense qu'il faut connaitre un minimum des caractéristiques de la bibliothèque. Si par exemple une bibliothèque permet de calculer 2 + 2, et qu'il y a pléthore d'allocation dynamique, écriture disque, communication réseau et que sais-je encore, est-ce vraiment une bonne bibliothèque pour quelque chose d'aussi simple que 2 + 2 ?
    Peut etre me suis-je mal exprimé... :

    Le propre d'une bibliothèque est de fournir un certain nombre de comportements reproductibles et dont le résultat est clairement prédictible en fonction des situations.

    S'il existe plusieurs possibilités pour fournir le service en question, et que les différentes possibilités présentent des performances différentes en fonction des situations, il est, effectivement, bon de savoir quel principe est utilisé par quel comportement de manière à pouvoir choisir celui qui sera le plus adapté à la situation à laquelle tu es confronté.

    Il reste par contre énormément de marge entre le fait de savoir quel principe est mis en oeuvre et le fait de savoir comment il a été mis en oeuvre

    La manière dont le principe ou le concept est mis en pratique, cela ne regarde absolument pas l'utilisateur final, pour la simple et bonne raison que le but même d'une bibliothèque est de libérer l'utilisateur de l'implémentation des services offerts par la bibliothèque

    En cela, on se doute bien qu'une bibliothèque permettant de fournir différents concepts de collections dynamiques va très certainement avoir recours à la gestion dynamique de la mémoire, mais l'utilisateur n'a absolument aucun besoin de s'en inquiéter pour pouvoir utiliser les collections en question (finalement, cela se rapproche très fort d'une application stricte de la loi demeter)
    Je n'utilise jamais l'électricité chez moi, car je ne branche jamais une prise de courant ??
    Là, tu prend un raccourci vraiment foireux (oserais-je le taxer de mauvaise foi )

    Ne serait-ce que parce qu'il ne suffit pas de brancher un appareil électrique pour consommer de l'électricité

    Le raisonnement serait beaucoup plus proche de quelque chose comme "je réduis ma consommation électrique car je n'utilise aucun appareil électrique"

    Eb outre, il y a une différence entre le fait de savoir que telle ou telle bibliothèque va utiliser l'allocation dynamique de la mémoire (mais avoir la certitude que la gestion dynamique de la mémoire est correctement prise en compte, car il ne faut pas oublier la libération qui doit survenir "en temps opportun" ) et celui de décider recourir soi-même à l'allocation dynamique dans son propre code !!

    Ne serait-ce que par les difficultés auxquelles on sera invariablement confrontés quant au choix du moment le plus oppoprtun pour libérer cette mémoire, en évitant les écueils dus aux tentative de double libération ou ceux dus aux fuites mémoire
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  19. #19
    Membre émérite
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Points : 2 274
    Points
    2 274
    Par défaut
    Absolument, le minimum pour utiliser une Lib c'est d'avoir à disposition une interface, ce qui est sous-jacent n'est pas forcément montré... Bien sûr, dans ce cas on peut faire du reverse, mais ça, c'est une autre histoire.

  20. #20
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2010
    Messages
    2 051
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 2 051
    Points : 877
    Points
    877
    Par défaut
    merci tous pour votre aide !!!!!!!

    Citation Envoyé par Jérôme_C Voir le message
    - Une classe calcul n'est pas forcément pertinent. S'il n'y a aucune données propre à ta classe, un namespace suffirait pour stocker tes claculs, non ?
    oui en effet, je comprends ce que tu veux dire pour la classe calcul (par contre un namespace je ne sais pas vraiment ce que c'est)

    Citation Envoyé par Jérôme_C Voir le message
    - Une classe peut avoir une autre classe en tant que membre, tu n'es pas obligé d'avoir seulement des types élémentaires. Ainsi, ta classe voiture peut avoir une association avec un carburant particulier.
    ah oui ! c'est quelque chose que j'avais oublié et qui est je pense très utile

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [EntLib4] déclaration des objets avec Unity
    Par cyrille37 dans le forum Général Dotnet
    Réponses: 1
    Dernier message: 24/03/2009, 23h34
  2. Sessions avant ou après déclaration des objets PHP
    Par tchoukapi dans le forum Langage
    Réponses: 2
    Dernier message: 22/08/2008, 23h38
  3. créer des objets dynamiquement
    Par lenul dans le forum C++
    Réponses: 17
    Dernier message: 03/04/2008, 06h35
  4. Des objets dynamiques?
    Par ben_ghost dans le forum VC++ .NET
    Réponses: 5
    Dernier message: 04/08/2006, 17h57
  5. [Convention]déclarations des objets
    Par allstar dans le forum Langage
    Réponses: 4
    Dernier message: 17/11/2005, 00h57

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