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 :

Fonction template virtuelle... comment l'éviter ?


Sujet :

Langage C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut Fonction template virtuelle... comment l'éviter ?
    Bonjour,

    Je viens d'apprendre à mes dépends un article de la norme C++ (14.5.2 p 3) : "A member function template shall not be virtual."

    En effet j'ai défini une classe CountedPointer template, un pointeur intelligent avec comptage de référence. Problème : je ne veux pas imposer à l'utilisateur de cette classe que la destruction du pointeur se traduise par un "delete" sur l'élément pointé. J'ai donc défini une virtuelle _destroyPointedObject() que l'utilisateur peut redéfinir :

    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
    template <class T>
    class CountedPointer
    {
    private:
    	int* _counter;
     
    	// Méthode virtuelle de destruction de l'objet pointé
    	virtual void _destroyPointedObject(T*);
     
    protected:
    	T* _pointedObject;
     
    public:
     
    // ..... plein de jolis constructeurs, destructeurs, opérateurs .....
     
    };
    Exemple d'utilisation pour un statement Oracle (API OCCI). On n'a pas le droit de deleter un statement Oracle, il faut le releaser avec terminateStatement, donc on dérive et on redéfinit _destroyPointedObject() :
    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
    class NamedStatement : public CountedPointer<Statement>
    {
    private:
    	string _name;
     
    	// Redéfinition de la méthode de destruction de l'objet pointé
    	void _destroyPointedObject(Statement*);
     
    public:
     
    // .... blablabla ....
     
    };
     
     
    void NamedStatement::_destroyPointedObject(Statement* pointedStatement)
    {
        pointedStatement->getConnection()->terminateStatement(pointedStatement);
    }

    Je sais maintenant que ce code est faux à cause de la règle sus-citée, qui fera que la méthode finalement appelée sera celle de la classe mère, et qu'on va donc deleter sauvagement notre statement. Mon problème est : comment faire autrement ?? Connaissez-vous une méthode "classique" permettant de s'affranchir rapidement de ce problème ?

    La seule solution que je vois serait d'imposer l'utilisation du delete, puis de passer par un adapteur qui encapsulerait le statement et appelerait terminateStatement() dans son desctructeur. Mais cela m'oblige à recoder toutes les classes que nous avons dérivée de ce CountedPointer jusqu'à présent (hé oui car comme tout bug mémoire qui se respecte, celui-ci ne s'est manifesté qu'après plusieurs mois à l'occasion d'une passe Purify). Si vous voyez une solution plus rapide en terme d'intégration, je suis preneur...

    Merci

  2. #2
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 749
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 749
    Points : 10 666
    Points
    10 666
    Billets dans le blog
    3
    Par défaut
    Je ne crois pas que ce code soit faux. Je pense que la norme parle plutot de fonctions membres templates:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class A
    {
    public:
        template<typename T>
        virtual void Test( const T & )
        {
        }
    };
    là ça me parraît compréhensible que ce soit déconseillé. D'ailleurs ça compile même pas sous VC++.
    Donc à priori tu n'as pas de problèmes. Celà dit quelques remarques sur ton code:
    * je verrais plutot une spécialisation partielle de template qu'un héritage avec surcharge virtuelle

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // supprimer le virtual
     
    // spécialiser pour Statement
    template<>
    void CountedPointer<Statement>::_destroyPointedObject(Statement* pointedStatement) 
    { 
        pointedStatement->getConnection()->terminateStatement(pointedStatement); 
    }
    * l'idée de l'adaptateur me parrait meilleure : on passe en 2° paramètre un "désalloueur" à utiliser

    * c'est ce que permet de faire différentes classes de pointeurs intelligents déjà existantes, parmi lesquelles boost::shared_ptr:
    http://www.developpez.net/forums/viewtopic.php?t=337244

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    Pourtant mon code doit bien être faux, car c'est vraiment la fonction de la classe mère qui est appelée. Ce n'est pas un problème de redéfinition, je constate ce problème sur toutes les classes filles.

    De plus j'ai essayé de la mettre virtuelle pure pour être sûr que je la redéfinis bien. Je la redéfinis bien puisque le compilo accepte d'instancier la classe fille, seulement surprise à l'édition des liens, j'ai un "unsatisfied symbol" sur la fonction en question !!

    Donc il y a bien un problème avec ce truc... Si quelqu'un a une idée plus précise sur l'identité du problème ?

    Pour la spécialisation partielle, j'avoue que je ne connaissais même pas, je vais me renseigner. Mais a priori, si ma méthode actuelle plante, j'ai peur que cette modif ne résolve rien...

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    Pour ce qui est des pointeurs intelligents de Boost je sais qu'ils existent, et je me passerai bien de faire ça moi même avec tous les risques qui vont avec... seulement mes chefs n'ont pas trop envie d'ajouter une API aux plate-formes de compil à ce stade du projet, donc sans autre besoin, pour l'instant on fait comme ça.

  5. #5
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 749
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 749
    Points : 10 666
    Points
    10 666
    Billets dans le blog
    3
    Par défaut
    Tu utilises quel compilateur ?

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    Citation Envoyé par Aurelien.Regat-Barrel
    Tu utilises quel compilateur ?
    aCC, sous HP-UX

    Je comprend mieux maintenant la spécialisation partielle. Par contre je jongle un peu pour arriver à passer entre les "duplicate symbol" et autres erreurs de ce genre, donc je pose carrément la question : où la spécialisation doit-elle, de préférence, être définie ?

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    Bon, finalement j'ai opté pour la méthode de l'adapteur... En écrivant le mot "foncteur" tu m'as mis la puce à l'oreille car comme un idiot de n'avais pas pensé à ça. Je comptais mettre en place un adapteur tel que décrit dans GotW pour adapter les tableaux aux auto_ptr. Mais là je suis le développeur du smart pointer, donc je vais pas me priver, j'opte pour la solution foncteur, exactement comme shared_ptr.

    Je vous tiens au courant

  8. #8
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    Tu devrais jeter un oeil aux SmartPtr de chez Loki, décrits dans Modern C++ design (chapitre dispo en ligne !). Ils sont faits à base de polices : tu peux configurer leur comportement assez finement avec des templates, pour les adapter à ce qu'ils vont stocker.

    Tu pourras trouver le lien et une petite explication dans mon tuto n°4 par exemple

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    Bingo !!! J'ai compris ce qui ne va pas dans mon code ! Aurelien tu avais raison, ce n'est pas un problème de template.

    J'ai ouvert mon livre "C++ Gotchas" presque par hasard sur le Gotcha #75 : "Calling virtual functions in constructors and Destructors". L'auteur y explique qu'en toute logique, étant donné qu'au moment ou le destructeur d'une classe de base est appelé la partie fille n'existe plus, si une fonction virtuelle est appelée, le programme ne pourra qu'exécuter celle de la classe mère... c'était pourtant évident ! Voilà pourquoi ma méthode ne pouvait pas marcher, puisque _destroyPointedObject() est bien sûr invoquée dans le destructeur, dans le cas où le compteur de références est à 0 après décrémentation.

    Concernant ce que j'ai observé en essayant avec une méthode virtuelle pure, l'auteur précise que "the standard specifies that the behavior of such a call is undefined". Dans mon cas ce fut un unsatisfied symbol.

    Je continue donc d'implémenter la solution par foncteur.

    Citation Envoyé par Loulou24
    Tu devrais jeter un oeil aux SmartPtr de chez Loki, décrits dans Modern C++ design (chapitre dispo en ligne !). Ils sont faits à base de polices : tu peux configurer leur comportement assez finement avec des templates, pour les adapter à ce qu'ils vont stocker.

    Tu pourras trouver le lien et une petite explication dans mon tuto n°4 par exemple
    Merci pour la référence, mais comme je le disais je ne peux pas l'utiliser. Je sais que les pointeurs intelligents existent déjà en plein d'exemplaires, mais la plateforme de compilation est déjà définie côté client, et on vient de me confirmer qu'aucun changement n'était possible à ce stade, du moins pas si on peut faire autrement. Je ne peux donc pas ajouter une librairie car, objectivement, je peux faire autrement.

  10. #10
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    Merci pour la référence, mais comme je le disais je ne peux pas l'utiliser. Je sais que les pointeurs intelligents existent déjà en plein d'exemplaires, mais la plateforme de compilation est déjà définie côté client, et on vient de me confirmer qu'aucun changement n'était possible à ce stade, du moins pas si on peut faire autrement. Je ne peux donc pas ajouter une librairie car, objectivement, je peux faire autrement
    Oui oui je sais. Je disais ça pour que tu t'en inspires (avis perso : c'est une très bonne source d'inspiration), pas pour que tu l'utilises tel quel.

  11. #11
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    OK merci pour le tuyau, j'irai voir comment sont faits leurs pointeurs intelligents

    En tout cas pour mon prochain projet C++ je crois que je commencerai par demander l'ajout de Boost aux plate-formes de dév... Je ne connaissais malheureusement pas cette API en arrivant sur ce projet.

  12. #12
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Si c'est après 2009, voire 2015 le temps que les compilos soient à jour, cette partie là de boost devrait être dans le standard.
    Je crois que c'est déjà dispo avec GCC 4, dans l'espace de noms tr1, et chez certains fournisseurs de SL.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  13. #13
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 68
    Points : 52
    Points
    52
    Par défaut
    Citation Envoyé par Luc Hermitte
    Si c'est après 2009, voire 2015 le temps que les compilos soient à jour, cette partie là de boost devrait être dans le standard.
    Je crois que c'est déjà dispo avec GCC 4, dans l'espace de noms tr1, et chez certains fournisseurs de SL.
    Ouaip, c'est ce que j'avais entendu dire, et ce sera l'argument principal auprès du management pour leur faire accepter d'intégrer cette API si toutefois elle n'est pas encore en standard dans le compilo utilisé...

    Je mets la balise résolu car j'ai implémenté avec succès la solution du foncteur, merci à tous pour votre aide

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

Discussions similaires

  1. Réponses: 52
    Dernier message: 23/10/2014, 11h22
  2. Comment redéfinir une fonction template ?
    Par Invité dans le forum Langage
    Réponses: 11
    Dernier message: 10/08/2014, 21h28
  3. Fonction template et classe virtuelle
    Par Trademark dans le forum Langage
    Réponses: 5
    Dernier message: 25/11/2011, 19h30
  4. Réponses: 1
    Dernier message: 12/01/2009, 16h45
  5. Fonctions template+friend sous VC7
    Par patapetz dans le forum MFC
    Réponses: 12
    Dernier message: 24/09/2004, 11h16

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