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

Langage C++ Discussion :

Classe générique et erreur "request for member [..] which is of non-class type"


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    24
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 24
    Par défaut Classe générique et erreur "request for member [..] which is of non-class type"
    Bonjour,

    Environnement :
    OS : Mac OS X 10.6.4
    IDE : Xcode 3.2
    Compilateur : GCC 4.2

    Je suis en train de me remettre au C++ après avoir eu un très léger apprentissage l'année dernière. Pour cela, je m'entraîne en implémentant les structures de données usuelles. J'ai donc commencé par la pile.

    Pour cela j'utilise donc une classe générique (template de classe) :

    Stack.h
    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
     
    #ifndef STACK_H_
    #define STACK_H_
     
    #include <iostream>
     
    template<typename T>
    class Stack {
    public: 
        Stack();
        virtual ~Stack();
     
        void push(const T);
        T pop();
        bool isEmpty() const;
        int size() const;
     
    private:
        T* _data;
        int _size;
        static void _arrayCopy(T*, T*, int);
    };
     
    #endif /* STACK_H_ */
    Stack.cpp
    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
     
    #include "Stack.h"
     
    using namespace std;
     
    template<typename T>
    Stack<T>::Stack() : _size(0) {
        _data = new T[0];
    }
     
    template<typename T>
    Stack<T>::~Stack() {
        delete _data;
    }
     
    template<typename T>
    void Stack<T>::push(const T element) {
        T* oldData = _data;
     
        _data = new T[++_size];
        Stack::_arrayCopy(oldData, _data, _size);
        delete oldData;
    }
     
    template<typename T>
    T Stack<T>::pop() {
     
        T element = _data[_size - 1];
     
        T* oldData = _data;
     
        _data = new T[--_size];
        Stack::_arrayCopy(oldData, _data, _size);
        delete oldData;
     
        return element;
    }
     
    template<typename T>
    bool Stack<T>::isEmpty() const {
        return _size == 0;
    }
     
    template<typename T>
    int Stack<T>::size() const {
        return _size;
    }
     
    template<typename T>
    void Stack<T>::_arrayCopy(T* source, T* destination, int copyCount) {
        for (int current = 0; current < copyCount; current++) {
            destination[current] = source[current];
        }
     
    }
    Afin de tester ma classe j'ai tout simplement commencé par l'instancier et j'appelle ensuite une méthode :

    main.cpp
    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
     
    #include <iostream>
    #include "Stack.h"
     
     
    using namespace std;
     
    int main () {
     
        Stack<int> pile();
        pile.isEmpty();
     
     
        return 0;
    }
    Mon problème est que mon compilateur ne semble pas reconnaître pile comme étant un objet de ma classe Stack<T> (en l'occurence, un objet de la classe Stack<int>).

    Voici l'erreur retournée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    
    /***/***/Workspace/containers/main.cpp:10:0
    /***/***/Workspace/containers/main.cpp:10:
    error: request for member 'isEmpty' in 'pile', which is of non-class type 'Stack<int> ()()'
    
    Ce qui me trouble dans ce message d'erreur, c'est que le compilateur mentionne une classe Stack<int> ()() (notez les deux paires de parenthèses).

    Étant donné que je manipule les classes génériques pour la première fois en C++, il est possible que mon erreur soit assez simple.

  2. #2
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Par défaut
    est le prototype d'une fonction nommée pile ne prenant aucun argument et renvoyant un objet du type Stack<int>.
    La bonne syntaxe pour instancier un objet sans lui passer d'arguments via le constructeur est
    . Quelqu'un sait si y'a une entré dans la FAQ pour ce problème ?


    Sinon, ta classe gère un pointeur, il font donc définir constructeur de copie et operator= (cf FAQ, tout ca ....) Et pas de template dans un fichier .cpp sinon tu vas avoir des problèmes avec l'édition des liens un jour (cf FAQ again ...)
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    24
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 24
    Par défaut
    OK pour l'appel du constructeur par défaut.

    Pour le problème de l'édition de liens causé par la définition des templates dans le .cpp, il s'est en effet produit. J'ai donc suivi le conseil de la FAQ (que je me souvient avoir lu hier soir en plus ).

    Mais le C++ me semble de plus en plus étrange :
    • Ne pas pouvoir définir des templates dans le .cpp
    • L'utilisation du terme méthode virtuelle pure pour ce que j'appelle une méthode abstraite
    • L'utilisation des méthodes virtuelles pour pouvoir utiliser le polymorphisme (j'ai l'impression que le mot-clé virtual ne sert dans ce cas qu'à palier à un problème dans le langage)


    Pour la surcharge des opérateurs, je suis en train de m'y atteler.

    Merci beaucoup.

  4. #4
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Bonjour,

    Quelqu'un sait si y'a une entré dans la FAQ pour ce problème ?
    Peut-être celle-ci?

    Mais le C++ me semble de plus en plus étrange :
    Ne pas pouvoir définir des templates dans le .cpp
    Il y a débat (voir la faq).
    Est-ce que quelqu'un sait si le mot-clé export est entré dans la norme?

    L'utilisation du terme méthode virtuelle pure pour ce que j'appelle une méthode abstraite
    L'utilisation des méthodes virtuelles pour pouvoir utiliser le polymorphisme (j'ai l'impression que le mot-clé virtual ne sert dans ce cas qu'à palier à un problème dans le langage)
    Je n'ai jamais entendu parlé de "méthode abstraite".
    Tu as entendu ça où?
    Le terme "classe abstraite" est par contre employé en C++ comme ailleurs (ex:Java).

    L'utilisation des méthodes virtuelles pour pouvoir utiliser le polymorphisme (j'ai l'impression que le mot-clé virtual ne sert dans ce cas qu'à palier à un problème dans le langage)
    On peut aussi le voir comme une plus grande liberté de conception : par exemple pour la gestion de la destruction en présence d'héritage (cf. faq).

  5. #5
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Par défaut
    Ne pas pouvoir définir des templates dans le .cpp
    Le compilo a besoin du corps des fonctions quand il instancie une classe template => faut tout mettre dans les header

    L'utilisation du terme méthode virtuelle pure pour ce que j'appelle une méthode abstraite
    méthode est assez peu utilisé dans le monde C++ien (sauf chez Stroustrup, qui lui faire correspondre la notion de fonctino membre virtuelle). On préfère parler de fonction qui peut être libre ou membre, ces dernières pouvant être qualifiées de constantes ou non, de virtuelle et même de virtuelle pure. A noter que ces derniers concepts sont orthogonaux. Une fonction membre constante virtuelle pure est tout à fait correct.
    L'utilisation des méthodes virtuelles pour pouvoir utiliser le polymorphisme (j'ai l'impression que le mot-clé virtual ne sert dans ce cas qu'à palier à un problème dans le langage)
    Tu aurais pas fait du Java (brr, j'en ai des frissons rien qu'a écrire le mot) avant ? Sinon cf ceci et un débat qu'il y a eu sur le forum C++.



    Est-ce que quelqu'un sait si le mot-clé export est entré dans la norme?
    Export a toujours été dans la norme C++03, il n'a juste jamais été supporté par la majorité des compilateurs. Je ne sais pas s'il est passé deprecated en C++0x.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,
    Ne pas pouvoir définir des templates dans le .cpp
    Pour être précis et complet:

    Il faut comprendre que le but des template est de se dire "je ne sais pas ce que je manipule, mais je sais comment je le manipule".

    Il s'en suit que l'on ignore, jusqu'à ce que l'on décide d'utiliser la fonction (ou la classe) avec une donnée d'un type précis quelle taille cette donnée va réellement prendre en mémoire.

    Le compilateur ne peut donc créer le jeu d'instructions que le processeur devra effectuer qu'une fois qu'il (le compilateur) sait... quel type de donnée est manipulé, et qu'il en connait donc la taille, et il aura donc besoin du code source de la fonction pour chaque appel de celle-ci

    Or, la "bonne pratique" qui consiste à ne pas inclure un fichier d'implémentation (*.cpp) avec une directive include est de stricte application, pour éviter d'avoir des problèmes à l'édition de liens (au moment où l'on regroupe les différents fichiers "objets" en un exécutable/ une bibliothèque), seuls l'inclusion de fichiers d'en-tête étant suggérée.

    En effet, un fichier d'en-tête a tendance à se propager "comme la peste": dés qu'il est inclus dans un autre fichier d'en-tête, il sera, par le jeu des inclusions en cascades, inclus dans tout fichier qui inclus le fichier d'en-tête qui l'inclus et ce, de manière directe ou indirecte.

    Comme c'est, en définitive, ce que l'on souhaite pour les fonctions (membres de classe ou de structures) template, il faut bien... définir les fonctions dans le fichier d'en-tête

    Une alternative peut consister à utiliser une autre extension pour la définition de ces fonctions, quelle qu'elle soit, pou autant que ce ne soit pas une extension connue pour représenter un type de fichier particulier.

    La directive #include n'est en effet absolument pas difficile quant au type de fichier qu'elle copie .

    Si tu tiens à séparer la déclaration des fonctions de leurs implémentation, tu peux parfaitement mettre l'implémentation dans un fichier portant l'extension *.impl (pour "implementation") ou tcc (pour "template code c++") ou ... tout ce que tu veux finalement

    Mais cela ne t'empêchera pas de devoir inclure ce fichier supplémentaire chaque fois que tu voudra... faire appel à une des fonctions déclarée dans le le fichier d'en-tête
    L'utilisation du terme méthode virtuelle pure pour ce que j'appelle une méthode abstraite
    Les termes, tu sais, c'est principalement fonction des conventions utilisées

    En C++, on fait une différence entre fonction (membre) et méthode parce que toute fonction membre n'est pas forcément virtuelle (susceptible d'être redéfinie dans une classe dérivée) contrairement à java par exemple où toute fonction est réputée virtuelle jusqu'à preuve du contraire (comprend: jusqu'à ce que le développeur dise explicitement qu'elle ne sera plus redéfinie avec le mot clé final).

    Il est donc "utile" de pouvoir déterminer si une fonction est virtuelle ou non simplement grâce au terme utilisé:
    • le terme fonction (membre) représente une fonction non virtuelle
    • le terme méthode est utilisé exclusivement pour représenter une fonction membre virtuelle
    Mais comme il n'est pas beaucoup plus long de parler de "fonction virtuelle" plutôt que de "méthode", les deux termes sont parfaitement interchangeable.

    La notion de abstraite Vs pure vient d'un raisonnement à peut près identique, et de la sémantique des mots

    Abstrait signifie "qui n'a pas (ou ne peut pas) avoir d'existence concrète". En programmation, on dirait d'une classe abstraite que l'on ne peut pas en créer une instance (un objet du type de la classe).

    Si l'on parle de fonction virtuelle pure au lieu de méhtode abstraite, c'est parce qu'une fonction virtuelle pure est bel et bien (et avant tout) une fonction qui peut (pire, qui doit) être (re)définie dans les classes dérivées.

    Simplement, et je répond à ton troisième point,
    L'utilisation des méthodes virtuelles pour pouvoir utiliser le polymorphisme (j'ai l'impression que le mot-clé virtual ne sert dans ce cas qu'à palier à un problème dans le langage)
    il n'a pas été possible de définir un comportement cohérent de base pour la fonction, sans doute parce que la classe de base ne dispose pas des informations nécessaires ou parce qu'il est impossible de trouver un comportement minimum commun à toutes les classes dérivées.

    Le mot clé virtual indique donc au compilateur que la fonction est destinée à être redéfinie pour les classes dérivées, mais on lui indique qu'il ne doit pas chercher l'implémentation de cette fonction pour la classe de base grâce au =0 qui suit la déclaration et qui rend la fonction virutelle... pure.

    Imaginons, simplement, que tu veuilles créer une hiérarchie de véhicules disposant de différents type de moteurs (essence, électriques, atomiques, ...).

    La classe de base serait un véhicule, et tout véhicule peut démarrer (comprend: lancer le moteur), mais chaque type de moteur va démarrer de manière différente:
    • un moteur à explosion utilisera un démarreur pour faire ses premiers tours
    • un moteur électrique nécessite simplement que l'on ferme le circuit électrique
    • un moteur nucléaire nécessite (je n'y connais rien dans le fonctionnement des réacteurs nucléaires, hein) sans doute que l'on introduise le composant radio actif dans une chambre particulière.
    • ...
    Quel comportement cohérent voudrais tu donc pouvoir définir pour un véhicule dont... tu ignores le type de moteur qu'il utilise

    La seule solution qui te reste en C++, c'est de dire, pour la classe Vehicule au compilateur "attention, la fonction démarrer existe bel et bien, et elle sera (re)définie pour toutes les classes dérivées concrètes, mais, actuellement, je ne dispose pas des informations pour te dire quoi faire".

    Au final, ta classe véhicule ressemblera à
    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
    class Vehicule
    {
        public:
        virtual    // la fonction est redéfinie pour les classes dérivées
        bool       // la fonction renvoie une valeur de réussite ou d'échec
        demarrer() // on ne parle pas de la fonction "accélérer"
        = 0 ;      // je n'ai pas de comportement à te donner pour cette fonction
                   // tu ne dois donc pas perdre ton temps à chercher son 
                   // implémentation pour la classe véhicule
        /* la suite ... */
    };
    class VehiculeExplosion : public Vehicule
    {
        public:
            /* ce qu'il faut, dont */
            virtual bool demarrer(); // ici, je sais ce que tu devra faire
    };
    class VehiculeElectrique : public Vehicule
    {
        public:
            /* ce qu'il faut, dont */
            virtual bool demarrer(); // ici, je sais ce que tu devra faire
    };
    class VehiculeNucleaire : public Vehicule
    {
        public:
            /* ce qu'il faut, dont */
            virtual bool demarrer(); // ici, je sais ce que tu devra faire
    };
    En retour, le compilateur qui a "horreur du vide" te dira "très bien, tu ne me donne pas le comportement à utiliser pour la classe Vehicule, mais, en retour, je t'interdit de créer une instance de véhicule (sans plus de précison), car autrement, rien ne t'empêchera d'essayer d'invoquer la fonction démarrer et mon collègue (l'éditeur de liens) ne saura plus à quel saint se vouer".
    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
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    24
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 24
    Par défaut
    Citation Envoyé par Aleph69 Voir le message
    Bonjour,
    Je n'ai jamais entendu parlé de "méthode abstraite".
    Tu as entendu ça où?
    Le terme "classe abstraite" est par contre employé en C++ comme ailleurs (ex:Java).
    Une méthode abstraite, en Java, est une méthode qui n'a pas de corps et dont la signature doit être précédée de la clause abstract. Elle doit être définie dans la classe fille.
    Une classe qui possède au moins une méthode abstraite est une classe abstraite et sa déclaration doit elle aussi être précédée de la clause abstract.
    C'est, à mon sens, la définition d'une méthode (fonction membre à ce que je viens de lire) virtuelle pure.

    Citation Envoyé par Davidbrcz Voir le message
    Tu aurais pas fait du Java (brr, j'en ai des frissons rien qu'a écrire le mot) avant ? Sinon cf ceci et un débat qu'il y a eu sur le forum C++.
    En effet, j'ai appris la POO avec le Java. J'ai ensuite eu droit à quelques cours de C++, mais pas suffisamment à mon goût.
    J'ai commencé à lire la FAQ (écrite par le créateur de C++ ?). Et j'avoue qu'elle est très utile pour comprendre les décisions prises par rapport à cette utilisation de la clause virtual.
    Cela doit sûrement permettre plus de flexibilité (comme l'a écrit Aleph69).

    Le C++ me paraît beaucoup plus complexe que le Java (dans le bon sens du terme, il à l'air de permettre de maîtriser le comportement des objects de manière beaucoup plus fine). Mais le Java me paraît justement plus adapté pour approcher les premiers concepts de la POO (je pense notamment à certains collègues d'IUT, qui auraient surement décroché si on les avait attaqué directement avec tous les concepts de C++).

    Merci pour toutes ces informations.

  8. #8
    Membre Expert
    Homme Profil pro
    Chercheur
    Inscrit en
    Mars 2010
    Messages
    1 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 218
    Par défaut
    Bonsoir,

    Citation Envoyé par damien.flament Voir le message
    Une méthode abstraite, en Java, est une méthode qui n'a pas de corps et dont la signature doit être précédée de la clause abstract. Elle doit être définie dans la classe fille.
    Une classe qui possède au moins une méthode abstraite est une classe abstraite et sa déclaration doit elle aussi être précédée de la clause abstract.
    C'est, à mon sens, la définition d'une méthode (fonction membre à ce que je viens de lire) virtuelle pure.
    Ah oui d'accord.
    Il y a une certaine cohérence aux deux terminologies.
    En Java, on distingue l'héritage (mot-clé extends) de l'implémentation (mot-clé implements).
    En C++, ces deux notions sont distinguées en encapsulant l'héritage.
    Un héritage public est une extension.
    Un héritage protégé ou privé est une implémentation.

    Par contre, je ne sais pas s'il existe une terminologie consacrée pour désigner les fonctions membres virtuelles pures/méthodes abstraites dans la théorie de la programmation objet.

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

Discussions similaires

  1. Réponses: 6
    Dernier message: 25/02/2013, 16h18
  2. Réponses: 2
    Dernier message: 17/02/2013, 20h59
  3. request for member
    Par annesophiedecar dans le forum C++
    Réponses: 2
    Dernier message: 11/10/2009, 21h20
  4. [C] request for member ". . ." in
    Par Meri Nose dans le forum C
    Réponses: 11
    Dernier message: 30/01/2009, 20h03
  5. Réponses: 14
    Dernier message: 14/09/2007, 17h28

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