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 :

Erreur compilateur sur liste doublement chaînée générique


Sujet :

C++

  1. #1
    Membre averti Avatar de MacPro
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    367
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Novembre 2007
    Messages : 367
    Points : 344
    Points
    344
    Par défaut Erreur compilateur sur liste doublement chaînée générique
    Bonjour,

    toujours dans ce projet j'ai maintenant une erreur de compilation.
    (je vous présente d'abord les extraits de code PUIS les questions)
    (dans les extraits suivants je coupe volontairement le code)

    J'ai créé un noeud
    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
     
    template <typename T>
    struct DNode
    {
        T val;				//!< Value for this node, can be anything
        DNode<T> *prev;			//!< Pointer to the previous node
        DNode<T> *next;			//!< Pointer to the next node
        // constructeur par défaut et copie mais pas destructeur et autres méthodes pratiques
        //! Destructor is useless because automatically called when deleting struct DNode, setting a destructor will cause calling ~T twice
        /*~DNode()
        {
        	//! Default destructor
        	//! @todo : if val = NULL, what happens when calling its destructor ?
        	val.~T();	// explicit call to the destructor
        }*/
    }
    puis la liste avec sentinelles
    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
     
    template <typename T>
    class DLList
    {
    	// typedef struct DNode<T> Node;	this could be used to simplify the writing
            friend class testDLList;        // provide access for a test class
            friend class testIterators;
    	private :
    		DNode<T>* head;		//! Handle on first data node (if any)
    		DNode<T>* tail;		//! Handle on last data node (if any)
    		DNode<T>* fore;		//! Handle on leading sentinel node
    		DNode<T>* aft;		//! Handle on trailing sentinel node
    		unsigned int m_size;	//! Holds the number of nodes
        // constructeurs, méthodes, destructeurs, itérateurs ...
    }

    Je vais utiliser cette liste pour stocker des pointeurs d'une classe TranslatableObserver disons donc quelque part j'ai ça en membre d'une classe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    DLList<TranslatableObserver*> list;
    Ensuite, j'ai l'erreur de compilation suivante :
    error: invalid conversion from ‘void*’ to ‘DNode<TranslatableObserver*>*’
    l'erreur se situe dans le destructeur de ma DLList donc voici le code :

    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
     
    template <typename T>
    DLList<T>::~DLList(void)
    {
    	// Deallocate all nodes, even the sentinels
    	DNode<T>* temp = NULL;
    	while(fore != NULL)
    	{
    		temp = fore;
    		fore = fore->next;
    		delete temp;
    	}
            // the following statement is not completely true :
            // Don't need to set head and tail to NULL nor m_size to 0 because we don't care right now
            head = NULL;
            tail = NULL;
            fore = NULL;
            aft = NULL;
            m_size = 0;
    }
    Le problème se situe sur les "= NULL", avec dans mon cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define NULL ((void*)0)
    Les questions :
    1 - Vous remarquez que j'ai volontairement dégagé struct DNode<T>::~DNode<T>() (ou tout simplement le destructeur d'un noeud).
    Lors de mes tests j'ai crée une classe 'personne' comportant un membre int et un tableau name avec allocation dynamique. Dans le destructeur de 'personne' je faisais un delete de 'name' sans mettre 'name' à NULL, chose que j'avais jugée inutile.
    Ensuite, je stockais une liste de 'personne', les noeuds étant donc des 'personne'.
    Je me suis aperçu lors du debug qu'en détruisant un noeud je passais deux fois dans le destructeur de 'personne' et puisque personne::name n'avait pas été mis à NULL la première fois alors plantage. La seule solution que j'ai trouvé a été d'enlever le destructeur d'un noeud pour éviter que struct DNode<T>::~T() soit appelé deux fois. Pourquoi est-il appelé deux fois ?

    2 - Pourquoi pour résoudre mon erreur de compilation je peux mettre 0 à la place de NULL ?
    Lorsque vous avez trouvé solution à votre problème, n'oubliez pas de cliquer en bas de la page
    Besoin d'un photographe de mariage : http://www.triangle-photo.fr

  2. #2
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 043
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 043
    Points : 2 234
    Points
    2 234
    Par défaut
    Pourquoi est-il appelé deux fois ?
    Dans ton destructeur de Noeud detient la ligne suivante :
    Mais T est une instanciation statique, si elle est dynamique, la destruction et la construction est à la charge du client tout comme les conteneurs standard, donc elle sera détruite toute seule à la fin de vie de ton nœud. Donc tu appelleras ton destructeur du nœud et le destructeur de T val; (qui ne devrais pas être mentionner dans ton destructeur) une maxime simple, 1 new = 1 delete. 0 new = 0 delete;

    Pourquoi pour résoudre mon erreur de compilation je peux mettre 0 à la place de NULL ?
    Je dirais même qui faut TOUJOURS mettre 0 et non pas NULL. NULL est une macro Windows, donc macro pas disponible sur toute les plateformes. Le standard c++ spécifie clairement qu'un pointeur qui ne veux pas être initialisé devra pointer sur l'adresse 0x00000000 sois 0 . Tu remarqueras donc que si delete 0 ne plante pas c'est que c'est prévu de faire comme ça
    Homer J. Simpson


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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Tu fais pas de placement new, alors n'appelle pas le destructeur explicitement.

    @Astraya : non NULL est standard et implementation defined, 0 ou 0L mais pas (void*)0 (ça c'est le NULL du C)
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  4. #4
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 043
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 043
    Points : 2 234
    Points
    2 234
    Par défaut
    @Goten : NULL étant une macro je doute fortement que ça sois standardisé. En C peut-être, en C++ non. Vision des macros selon Stroustrup, sur la macro NULL, petite histoire de NULL. Autant oublié NULL en C++
    Homer J. Simpson


  5. #5
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Astraya Voir le message
    @Goten : NULL étant une macro je doute fortement que ça sois standardisé. En C peut-être, en C++ non. Vision des macros selon Stroustrup, sur la macro NULL, petite histoire de NULL. Autant oublié NULL en C++
    Stroupstrup (et ce n'est pas le seul, loin de là) n'aime peut être pas les macros et NULL, mais il n'empêche que NULL fait bel et bien parti du standard C++ :

    4 The macro NULL is an implementation-defined C + + null-pointer constant in this International Standard
    Et au passage le lien sur la macro NULL dit bien que NULL existe.

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

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Citation Envoyé par Astraya Voir le message
    @Goten : NULL étant une macro je doute fortement que ça sois standardisé. En C peut-être, en C++ non. Vision des macros selon Stroustrup, sur la macro NULL, petite histoire de NULL. Autant oublié NULL en C++
    Norme 18.1.4 :

    The macro NULL is an implementation-defined C + + null pointer constant in this International Standard
    (4.10).180)
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  7. #7
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 043
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 043
    Points : 2 234
    Points
    2 234
    Par défaut
    Ok je sais pas. Étrange tout de même d'accepter cet héritage du C.

    Ceci étant les macros de ce type sont tout de même à oublier. Et apparemment, le seras ( ou une alternative sera présenté ) dans le prochain standard, mot clé nullptr qui pourras être convertis en n'importe qu'elle type de pointeur.
    Homer J. Simpson


  8. #8
    Membre averti Avatar de MacPro
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    367
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Novembre 2007
    Messages : 367
    Points : 344
    Points
    344
    Par défaut
    OK, donc jusque là on a résolu l'histoire de la destruction du membre template T cf. :

    Mais T est une instanciation statique, si elle est dynamique, la destruction et la construction est à la charge du client tout comme les conteneurs standard, donc elle sera détruite toute seule à la fin de vie de ton nœud. Donc tu appelleras ton destructeur du nœud et le destructeur de T val; (qui ne devrais pas être mentionner dans ton destructeur) une maxime simple, 1 new = 1 delete. 0 new = 0 delete;

    Je fais une parenthèse :

    @goten :

    Tu fais pas de placement new, alors n'appelle pas le destructeur explicitement.
    Est-ce que tu peux développer ? J'ai vu dans les sources de Qt la chose suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template <class Key, class T>
    struct QMapNode {
        Key key;
        T value;
     
    private:
        // never access these members through this structure.
        // see below
        QMapData::Node *backward;
        QMapData::Node *forward[1];
    };
    puis pour créer un nouvel objet on voit bien le placement new utilisant le constructeur de copie, mais pourquoi pas l'affectation dans ce cas ?:
    à l'instant où l'objet (le noeud est créé dynamiquement) à quoi est initialisé son membre T, initialisation par constructeur par défaut ou copie ou aucun d'eux ?

    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
     
    template <class Key, class T>
    Q_INLINE_TEMPLATE typename QMapData::Node *
    QMap<Key, T>::node_create(QMapData *adt, QMapData::Node *aupdate[], const Key &akey, const T &avalue)
    {
        QMapData::Node *abstractNode = adt->node_create(aupdate, payload(), alignment());
        QT_TRY {
            Node *concreteNode = concrete(abstractNode);
            new (&concreteNode->key) Key(akey);
            QT_TRY {
                new (&concreteNode->value) T(avalue);
            } QT_CATCH(...) {
                concreteNode->key.~Key();
                QT_RETHROW;
            }
        } QT_CATCH(...) {
            adt->node_delete(aupdate, payload(), abstractNode);
            QT_RETHROW;
        }
        return abstractNode;
    }
    // fin de la parenthèse

    pour en revenir à mon problème : error: invalid conversion from ‘void*’ to ‘DNode<TranslatableObserver*>*’ any idea ?
    Mon point de vue est que NULL était un ((void*)0) ou plus simplement un void* et il n'arrive pas à caster ça en DNode<TranslatableObserver*>* ? Qu'est-ce qui fait qu'il se démerde mieux avec un '0' ?
    Lorsque vous avez trouvé solution à votre problème, n'oubliez pas de cliquer en bas de la page
    Besoin d'un photographe de mariage : http://www.triangle-photo.fr

  9. #9
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Astraya Voir le message
    Ok je sais pas. Étrange tout de même d'accepter cet héritage du C.
    Le but était de garder une assez grande compatibilité avec le C. Supprimer NULL aurait probablement rendu trop de code incompatible.

    Citation Envoyé par Astraya Voir le message
    Ceci étant les macros de ce type sont tout de même à oublier. Et apparemment, le seras ( ou une alternative sera présenté ) dans le prochain standard, mot clé nullptr qui pourras être convertis en n'importe qu'elle type de pointeur.
    Je n'ai pas vu mention de la suppression de NULL (à priori, ça casserait trop de code) dans les draft C++0x.

    Maintenant effectivement, l'utilisation de NULL n'est pas une pratique que je conseillerais en C++. Mais je ne trouve pas que l'utilisation de 0 soit vraiment plus satisfaisante dans la majorité des cas (car on perd l'information "est un pointeur").
    De prime abord, l'introduction de nullptr me semble une bonne idée puisqu'elle évite l'utilisation de la macro NULL tout en conservant la qualité documentaire de celle-ci.

  10. #10
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par MacPro Voir le message
    pour en revenir à mon problème : error: invalid conversion from ‘void*’ to ‘DNode<TranslatableObserver*>*’ any idea ?
    Mon point de vue est que NULL était un ((void*)0) ou plus simplement un void* et il n'arrive pas à caster ça en DNode<TranslatableObserver*>* ? Qu'est-ce qui fait qu'il se démerde mieux avec un '0' ?
    En C++, NULL ne devrait pas être un (void*)0. Si tel est le cas, soit l'implémentation que tu utilises n'est pas correcte, soit NULL est redéfini quelque part (un des risques cités par Astraya lié à l'utilisation des macros).
    Dans les deux cas, il y a un souci.

    Avec '0', il ne devrait pas mieux fonctionner. Par contre avec 0 cela doit fonctionner, car 0 est une valeur correcte pour initialiser un pointeur.

  11. #11
    Membre averti Avatar de MacPro
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    367
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Novembre 2007
    Messages : 367
    Points : 344
    Points
    344
    Par défaut
    Citation Envoyé par gl Voir le message
    Avec '0', il ne devrait pas mieux fonctionner. Par contre avec 0 cela doit fonctionner, car 0 est une valeur correcte pour initialiser un pointeur.
    Exact, j'ai mal écris dans mon post, je pensais à la valeur numérique 0 et non au code ASCII de '0'.

    Je sais que 0 est une valeur correcte pour initialiser un pointeur, je ne sais par contre ce que ce (void*)0 faisait là, ça avait l'air tellement réfléchi que je me suis dit que y'avait peut-être une raison

    Bon, le topic est résolu, Merci les gars.
    Lorsque vous avez trouvé solution à votre problème, n'oubliez pas de cliquer en bas de la page
    Besoin d'un photographe de mariage : http://www.triangle-photo.fr

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

Discussions similaires

  1. Liste doublement chainée générique, erreurs !
    Par Virus721 dans le forum C++
    Réponses: 4
    Dernier message: 05/11/2010, 13h47
  2. liste doublement chaînée: tutorial ou cours sur sites internet
    Par johnny3 dans le forum Débuter avec Java
    Réponses: 2
    Dernier message: 12/05/2008, 02h34
  3. Réponses: 11
    Dernier message: 21/03/2008, 22h46
  4. Liste doublement chaînée
    Par garf dans le forum Langage
    Réponses: 3
    Dernier message: 27/09/2005, 09h33

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