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 :

Problème de const


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    226
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 226
    Points : 39
    Points
    39
    Par défaut Problème de const
    Bonjour
    je suis entrain d'apprendre le c++ et j'ai aujourdhui un exercice ou je dois appeler une fonction qui prend un tableau de const const
    voila le prototype de la fonction qui est imposé par l'exercice.

    La class Object est definie dans un autre fichier .h et elle est correctement inclu dans le fichier main.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void organize(Object const * const * Object_packet)
    quand j'essaye d'appeler la fonction
    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
    int main()
    { 
      Object ** box = new Object * [10];
     
      box[0] =  new Object;
      box[1] = new   Object;
      box[2] = new Object;
      box[3] = new Object;
      box[4] = new Object;
      box[5] = new Object;
      box[6] = new Object;
      box[7] = new Object;
      box[8] = new Object;
      box[9] = NULL
     
      organize(box);
    }
    j'ai une erreur lors de la compilation

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Main.cpp erreur*: cannot call member function ‘void organize(const Object* const*)’ without object
    Pouvez vous m'aider ?
    Merci d'avance

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Ton compilateur est explicite: on ne peut pas appeler une fonction membre sans objet.

    une fonction membre s'appelle sur un objet du type de la classe dans laquelle elle est définie.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

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

    Informations professionnelles :
    Activité : aucun

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

    Hou la, la... Ce que tu essayes de faire là est, à tout le moins, foireux!!!

    Du point de vue conceptuel, tout d'abord : le fait que tu essayes de travailler avec une classe Object me donne tout lieu de croire que tu essayes désepérément de faire tenir ensemble une très grande quantité d'objet qui... n'ont rien à voir entre eux, au mépris total du principe de substitution de Liskov, qui est LE principe de base à respecter IMPERATIVEMENT lorsqu'il s'agit d'héritage.

    Ta classe Object me fait effectivement "naturellement" penser à ce que l'on appelle généralement un "god object" : une classe qui essaye de correspondre à un tas d'éléments qui n'ont rien à voir entre eux et qui ne devraient donc -- si l'on respectait le LSP -- avoir aucune utilité, vu qu'il serait totalement impossible de trouver le moindre comportement qui puisse être considéré comme "commun à touts les types dérivés".

    Je ne sais pas quel est ton projet au final, mais, pour te permettre de te faire une idée, cette classe va essayer de faire cohabiter des outils (marteaux, tournevis, ...), des véhicules (bateaux, avions, voitures) et des animaux(veaux, vaches, chevre, moutons). Poses toi dés lors la question : "y a-t-il la moindre chance de trouver ne serait-ce qu'un seul comportement -- quitte à l'adapter au cas par cas -- qui puisse s'appliquer à tous ces éléments ". Si tu dois être totalement honnête, tu te rendras compte que non

    Ensuite, l'idée même de travailler avec des pointeurs de pointeurs et de gérer manuellement la mémoire permettant de représenter un tableau (des éléments "contigus en mémoire") est une pure aberration en C++, car tu n'arriveras jamais à travailler "proprement" et "de manière sécurisée".

    La bibliothèque standard (la SL, dont nous pouvons ne prendre en compte, dans le cas présent, que le le sous ensemble implémenté à base de template : la STL) nous fournit plein de solutions destinées à nous faciliter la vie sur ce coup là :

    • Si tu connais le nombre d'éléments dont tu as besoin à la compilation, tu devrais te tourner vers la classe std::array (disponible par inclusion du fichier d'en-tête <array>), et utiliser un code proche de std::array<Object *, 10> myArray;
    • Si le nombre d'éléments que ton tableau doit contenir ne sera connu qu'à l'exécution, tu devras sans doute te tourner vers la classe std::vector (disponible par inclusion du fichier d'en-tête <vector>). Reportes-toi à la documentation pour en savoir plus sur l'utilisation de cette classe
    • Enfin, d'autres collections pourraient s'avérer utiles, en fonction de tes besoins, telles que std::stack, std::queue, std::deque, std::list, std::set ou std::map (en plus d'un certain nombre que je décide de ne pas citer ici).

    Enfin, il faut savoir que new est toujours susceptible d'échouer, et, lorsque cela arrive, il lance une exception de type std::bad_alloc. Dans le cas présent, cela ne posera sans doute jamais de problème, mais autant d'habituer directement à travailler de manière sécurisée : si une exception est lancée (quelle qu'en soit la raison), l'application va remonter la pile d'appel de toutes les fonctions afin de trouver un bloc try... catch susceptible de récupérer l'exception en question.

    Si une allocation dynamique a été effectuée dans l'une des fonctions qui se trouve dans la pile d'appel, nous courrons le risque de perdre l'adresse mémoire correspondant au pointeur qui permet d'accéder à l'élément en question, ce qui aura pour résultat... de nous empêcher de libérer correctement la mémoire allouée. On appelle le fait de ne plus être en mesure de libérer correctement la mémoire allouée par new parce que l'on a perdu (pour n'importe quelle raison, dont font partie les exceptions) le pointeur associé à l'adresse mémoire en question une fuite mémoire.

    Si ce phénomène se reproduit trop souvent, ce n'est pas seulement la stabilité de ton application qui risque d'être compromise, mais carrément l'ensemble du système sur lequel elle s'exécute, car toute ressource utilisée est... indisponible pour l'ensemble du système.

    Tu comprendras donc qu'il faille prendre un maximum de mesures pour éviter ce genre de problèmes. Par chance, la STL nous fournit également des outils sympa dans ce domaine. On les appelle généralement pointeurs intelligents (smart pointers).

    Et leur utilisation est très largement conseillée. Tu peux choisir entre std::unique_ptr et le couple std::shared_ptr / std::weak_ptr en fonction de tes besoins

    Enfin, il se peut que je me sois trompé en ce qui concerne ta classe Object, et qu'elle n'a en réalité rien à voir avec la notion de "god object".

    Après tout, le fait que tu fasse un = new Object; laisse à penser que ta classe Object est -- à tout le moins -- une classe concrète, alors qu'un "god object" entrerait généralement dans la catégorie des "classes abstraites".

    Dans ce cas, devrais au minimum essayer de lui donner un sens plus "précis". Appelle la "Vehicle" si elle regroupe "n'importe quel type" de véhicules, "Character" si elle regroupe "n'importe quel type" de personnage, "Person" si elle regroupe "n'importe quel type" de personnes, "Animal" si elle regroupe n'importe quel type d'animaux...

    Bref, donne lui un nom qui puisse, à sa seule lecture, faire comprendre précisément et sans l'ombre d'un doute au lecteur à quel type d'élément il a affaire. Cela facilitera la vie de tout le monde, et la tienne en premier.

    En outre, l'allocation dynamique (de préférence, associée au pointeurs intelligents) n'a réellement de sens que pour les éléments qui ont sémantique d'entité; pour les types de données qui sont destinés à faire partie d'une "hiérarchie de classes".

    Tu remarqueras, en effet, que tous les exemples que j'ai peu donné plus haut (Vehicle, Character, Animal, Person) représentent une "généralisation" de types plus complets:
    • un véhicule peut être (au choix) un bateau, un avion, une moto, ou une voiture;
    • un personnage peut être (au choix) un joueur, un pnj ou un personnage "de décors";
    • une personne peut être (au choix) un client, un fournisseur, ou un employé;
    • un animal peut être (au choix) un lion, un tigre, un chat, un lapin ou une chêvre;
    • ...

    Pour les types de donnée présentant une sémantique de valeur (point, date, heure, position, quantité, prix, ...), il est généralement totalement inutile d'avoir recours à l'allocation dynamique de la mémoire

    Oui, je sais : beaucoup de langages -- en allant de java à C#, en passant par python -- utilisent systématiquement le mot clé new lorsqu'il faut créer une variable. C'est à cause de leur "mécanique interne", qui est totalement différente du C++. Ici, nous codons en C++, et, comme le mot clé new a des effets "totalement différents" de ceux que l'on retrouve avec les autres, langages, il faut bien que l'on adapte également les habitudes liées à son utilisation

    Une fois que tu auras corrigé les quelques points évoqués (n'hésite pas à demander plus de précision en cas de besoin), nous pourrons avancer dans la bonne direction. Et il se peut même que tu trouves la solution "tout seul"
    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

  4. #4
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Citation Envoyé par ternel
    Ton compilateur est explicite: on ne peut pas appeler une fonction membre sans objet.

    une fonction membre s'appelle sur un objet du type de la classe dans laquelle elle est définie.
    Donc la fonction organize() de ton exercice a été déclarée par erreur dans une classe, ou bien il faut l’appeler en tant que membre de cette classe, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
       Object obj;
       obj.organize( box );

  5. #5
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 470
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 470
    Points : 6 108
    Points
    6 108
    Par défaut
    @koala01 : vodkline a écrit « voila le prototype de la fonction qui est imposé par l'exercice ». Ce n'est donc pas lui qui a choisi le nom Object.
    A mon avis, le but de l'exercice n'est pas d'apprendre les hiérarchies de classes, mais de jouer avec les pointeurs. Et quand le prof a voulu nommer « truc qu'on peut ranger », le premier mot qui lui est passé par la tête est Object.
    A part ça, tes remarques sont pertinentes, mais je ne sais pas si vodkline pourra les comprendre.

    @vodkline : Voici un exemple de bout de code qui compile :
    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #include <iostream>
     
    class Object {};
     
    class Bidule {
    public:
        void organize(Object const * const * Object_packet) {
            std::cout << "Appel à Bidule::organize\n";
        }
    };
     
    const size_t cBOX_SIZE = 9;
        // Dans un gros programme, le c minuscule ici servirait à éviter une collision avec un nom de macro,
        // dans le cas où quelqu'un aurait déjà défini quelque part une macro BOX_SIZE sans qu'on ne l'ait remarquée.
        // Par convention, les noms des macros sont entièrement en majuscules.
     
    int main()
    {
        Object box[cBOX_SIZE]; // tableau de cBOX_SIZE objects
            // Cette instruction alloue cBOX_SIZE*sizeof(Object) bytes dans la pile et appelle cBOX_SIZE fois le constructeur par défaut de Object.
            // Allouer dans la pile est plus performant qu'allouer dans la mémoire dynamique avec new.
            // En plus, cela permet de ne pas devoir appeler delete : les objets seront détruits quand on sortira de la portée de la variable automatique box.
     
        Object const* packet[cBOX_SIZE+1];
        for(size_t k = 0; k < cBOX_SIZE; ++k)
            packet[k] = &box[k]; // Remarque : autre écriture : packet[k] = box + k;
        packet[cBOX_SIZE] = NULL; // Remarque : en C++11, il sera préférable de remplacer NULL par nullptr.
        Object const* const* packetAsPtr = packet;
     
    //  organize(packetAsPtr); // Ne compile pas. Comme l'a dit ternel : on ne peut pas appeler une fonction membre sans objet.
     
        Bidule bidule;
        bidule.organize(packetAsPtr); // Compile.
     
        return 0;
    }
    Remarques complémentaires déjà dites par koala01, mais reformulées à ma sauce :
    • Les tableaux natifs du C++ sont utiles à connaître, car on peut en rencontrer dans du code existant. Mais, quand tu connaîtras les conteneurs de la STL, il vaudra mieux utiliser ces derniers. Par exemple, std::array possède toutes les fonctionnalités des tableaux natifs du C++, avec des avantages en plus.
    • Quand tu verras les exceptions et les blocs try-catch, tu constateras que, avec des instructions de la forme pointeurNu = new MaClasse, un code qui assure que delete pointeurNu est toujours appelé est lourd. Les pointeurs intelligents comme std::unique_ptr sont une solution. Mais, tout ça, tu le verras plus tard.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Pyramidev Voir le message
    @koala01 : vodkline a écrit « voila le prototype de la fonction qui est imposé par l'exercice ». Ce n'est donc pas lui qui a choisi le nom Object.
    A mon avis, le but de l'exercice n'est pas d'apprendre les hiérarchies de classes, mais de jouer avec les pointeurs. Et quand le prof a voulu nommer « truc qu'on peut ranger », le premier mot qui lui est passé par la tête est Object.
    Ouppss... j'avais zappé cette phrase...

    Dieux que je hais ces profs qui essayent de donner un cours de soit-disant C++ en apprenant des pratiques exclusivement issues du C, et qui sont -- de plus -- des nids à misères en C++.
    A part ça, tes remarques sont pertinentes, mais je ne sais pas si vodkline pourra les comprendre.
    Ben, s'il ne les comprend pas, et qu'il souhaite les comprendre, il pourra toujours demander des précisions... Nous nous ferons un plaisir de les lui donner
    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

  7. #7
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    C'est déjà pas trop mal qu'il ait pensé (le prof) à rendre const tous les niveaux de pointés.
    Ce en qui, il aura peut-être une surprise... car si un T* est convertible en T const*, il n'y a pas de conversion implicite de T, même si celui-ci est aussi un pointeur (ici Objet*)
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

Discussions similaires

  1. Problème de const et de pointeur dans une méthode
    Par Vonziz dans le forum Débuter
    Réponses: 15
    Dernier message: 18/09/2008, 14h41
  2. Problème de const
    Par mister3957 dans le forum C++
    Réponses: 2
    Dernier message: 03/03/2008, 12h44
  3. problème avec const correctness
    Par donkeyquote dans le forum C++
    Réponses: 5
    Dernier message: 12/10/2007, 01h55
  4. Problèmes avec const ?
    Par oodini dans le forum C++
    Réponses: 11
    Dernier message: 16/02/2007, 15h48
  5. problème avec const char * et #define macro
    Par CodeurNé dans le forum C
    Réponses: 5
    Dernier message: 20/09/2006, 21h25

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