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 :

[Débutant]Foncteur et operator() template


Sujet :

Langage C++

  1. #1
    Sub
    Sub est déconnecté
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2003
    Messages : 20
    Par défaut [Débutant]Foncteur et operator() template
    Bonjour à tous.

    Je me suis récemment mis en tête de développer quelques petites classe pour faciliter le développement de mes programmes. Je suis développeur amateur(ainsi que débutant) et par conséquent c'est volontaire que je réinvente la roue.

    J'ai donc décidé de me faire une class New qui compterait le nombre de bytes alloué pour vérifier qu'il n'y a pas de fuite mémoire.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class New
    {
        public :
            template<class T> T* operator() (void);
    };
    template<class T> T* New::operator() (void)
    {
        return new T;
    }
    Et je pensais pouvoir l'utiliser ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        int* n = New<int>();
     
        return EXIT_SUCCESS;
    }
    Et voilà donc les erreurs que j'obtiens :

    error: expected primary-expression before "int"
    error: expected `,' or `;' before "int"

    Ma question est donc simple : Quel chapitre sur les templates et les foncteurs ai-je loupé ?

  2. #2
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par défaut
    Tu appelles le constructeur, et pas l'operator() comme tu le voudrais.
    Il faut écrire à la place qqch du genre:
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    New<int> alloc;
    int* n = alloc();
    Ce que tu souhaites faire ressemble aux allocateurs de la STL... (enfin eux ne recomptent pas le nombre d'octets alloués, je vois pas comment faire d'ailleurs).
    Une fuite mémoire, c'est quand la mémoire n'est pas libérée, donc revérifier la taille de la mémoire allouée n'aide pas à contrôler les fuites. 'new' renvoit à la place une execption si y'a un problème (plus assez de mémoire)


    PS: et puis je m'aperçois que tu déclares mal ta classe New: tu devrais plutôt déclarer te classe template, et pas la fonction membre.
    (J'ai jamais essayé mais ça doit être coton d'utiliser un operator() avec des paramètres templates)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<class T> class New()
    {
      T *operator()() const {...}
    };

  3. #3
    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 : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    A ma connaissance il n'y a pas moyen de spécifier de paramètres template lorsque tu appelles un opérateur surchargé sous sa forme "courte" (tu pourrais par contre l'appeler ainsi j'imagine : New().operator()<int>()).

    Le mieux c'est encore d'oublier la surcharge d'opérateur et d'utiliser une fonction avec un joli nom explicite (Allocate() par exemple).

    Ou encore de surcharger les opérateurs new / delete directement.

  4. #4
    Sub
    Sub est déconnecté
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2003
    Messages : 20
    Par défaut
    Charlemagne >

    Eh bien en fait là j'ai juste mis la partie de la classe qui me pose problème, en réalité la fonction operator() est surchargée ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template<class T> T* Truc::CNew::operator() (void)
    {
    	mNumberOfNew++;
    	mNumberOfBytesAllocated += sizeof(T);
    	return new T;
    }
    Et à côté j'ai une class Delete qui surcharge operator() ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template<class T> void Truc::CDelete::operator() (T& objectToDelete)
    {
    	mNumberOfDelete++;
    	mNumberOfBytesDeallocated += sizeof(T*);
    	delete objectToDelete;
    }
    Et après par soustraction j'en déduis s'il y a eu autant de new que de delete et si le nombre de bytes alloué est le même que le nombre désaloué.

    Si c'est la classe que je déclare template, je serais obligé d'instancier un objet pour chaque type ce qui est tout sauf efficace de mon point de vue.

    Laurent Gomila >

    Après essai, New().operator()<int>() fonctionne effectivement mais du coup cela casse l'intérêt de ce que j'avais en tête, une fonction courte et pas trop moche. J'ai également pensé à la surcharge de new et delete mais je trouvais cela un peu trop "crade" sans vouloir te vexer (car j'ai lu ton excellent tutorial) et puis en plus j'ai pas encore le niveau pour m'aventurer dans ce genre de surcharge. Je vais donc me rabattre sur une fonction allocate d'une classe MemoryManager.

    Merci à vous deux.

  5. #5
    Membre émérite Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Par défaut
    Pendant que tu y est, tu peux faire hériter ta classe template MemoryManager d'une classe de base MemoryCounters (qui elle n'est pas template). Dans le but de stocker tes compteurs (qui sont je l'imagine communs à tous les types de variables allouées).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    class MemoryCounters {
      protected:
        int nb_bytes_allocateds;
        int nb_bytes_freeds;
    };
     
    template<typename T>
    class MemoryManager : public MemoryCounters {
       ...
       T* Allocate(...);
       ...
    };

  6. #6
    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 : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Pendant que tu y est, tu peux faire hériter ta classe template MemoryManager d'une classe de base MemoryCounters (qui elle n'est pas template). Dans le but de stocker tes compteurs (qui sont je l'imagine communs à tous les types de variables allouées).
    Ce n'est pas le gestionnaire de mémoire qui est template, seulement ses fonctions new / delete

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class T> void Mouuh::CDelete::operator() (T& objectToDelete)
    {
    	mNumberOfDelete++;
    	mNumberOfBytesDeallocated += sizeof(T*);
    	delete objectToDelete;
    }
    Ca n'a pas l'air correct. Pourquoi pas comme ça ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class T> void Mouuh::CDelete::operator() (const T* objectToDelete)
    {
    	mNumberOfDelete++;
    	mNumberOfBytesDeallocated += sizeof(T);
    	delete objectToDelete;
    }
    A noter aussi qu'avec ce code tu vas te faire berner lorsque le polymorphisme entrera en jeu (tu vas allouer un Derivee* de X octets et le libérer sous forme de Base* qui ne fera que Y octets).

  7. #7
    Membre émérite Avatar de mchk0123
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    816
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2007
    Messages : 816
    Par défaut
    Citation Envoyé par Laurent Gomila
    Ce n'est pas le gestionnaire de mémoire qui est template, seulement ses fonctions new / delete
    J'avais sauté une étape...

    A noter aussi qu'avec ce code tu vas te faire berner lorsque le polymorphisme entrera en jeu (tu vas allouer un Derivee* de X octets et le libérer sous forme de Base* qui ne fera que Y octets).
    Effectivement dans cette situation là :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Base {
    };
     
    classe Derivee : public Base {
    };
     
    CNew allocator;
    CDelete destructor;
    Base *ptr = allocator.operator()<Derivee>();
    ...
    destructor.operator()<Base>(ptr);
    Il y aura pb.

    Autre truc, tu peux rendre moins "crade" l'utilisation de l'opérateur () via des macros :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #define NEW(Type) (allocator.operator()<Type>())
    #define DEL(Type, Ptr) (destructor.operator()<Type>(Ptr))
     
    int *ptr = NEW(int);
     
    DEL(int, ptr);

  8. #8
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par défaut
    Franchement, je vois pas l'intérêt de passer un paramètre template à l'operator() quand bien même il lui donnerait un "joli nom explicite".
    Qu'il prenne modèle sur les allocateurs.
    Pour moi le paramètre template doit être celui de la classe, surtout si c'est sinon pour se farcir des syntaxes du genre "allocator.operator()<Derivee>()", avec ou sans macro...
    Quelqu'un peut éclairer ma lanterne?

    PS: Je déconseille également de surcharger les opérateurs new et delete. C'est "dangereux", les syntaxes sont vraiment particulières, et de plus (sans avoir approfondit la question) il me semble bien avoir vu des implémentations de la STL qui faisaient leur propre surcharge...

  9. #9
    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 : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    Franchement, je vois pas l'intérêt de passer un paramètre template à l'operator() quand bien même il lui donnerait un "joli nom explicite".
    Qu'il prenne modèle sur les allocateurs.
    Pour moi le paramètre template doit être celui de la classe, surtout si c'est pour se farcir des syntaxes du genre "allocator.operator()<Derivee>()", avec ou sans macro...
    Quelqu'un peut éclairer ma lanterne?
    On est d'accord pour dire que ça aboutit à une syntaxe insensée.
    On est d'accord aussi pour dire que passer la classe en template n'arrangerait pas grand chose.

    Je déconseille également de surcharger les opérateurs new et delete. C'est "dangereux", les syntaxes sont vraiment particulières, et de plus (sans avoir approfondit la question) il me semble bien avoir vu des implémentations de la STL qui faisaient leur propre surcharge...
    Pas dangereux, non. Du moment qu'on ne fait rien d'idiot le comportement est parfaitement défini.
    Concernant la syntaxe, il n'y a rien de particulier là-dedans :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void* operator new(std::size_t Size);
    void operator delete(void* Ptr);
    Et pour éviter les conflits on peut même utiliser des versions dont on est sûr que personne d'autre ne se servira :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct my_new_t {};
    my_new_t my_new;
     
    void* operator new(std::size_t Size, my_new_t);
     
    int* x = new(my_new) int;
    Par contre impossible à faire pour delete, donc faudra un peu de gymnastique supplémentaire.

    L'avantage par rapport à un bon vieux my_alloc<int>(), c'est qu'on pourra toujours spécifier des paramètres pour construire l'objet.

  10. #10
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par défaut
    C'est bien beau tout ça, mais c'est plus que compliqué pour faire ce qui n'est ni plus ni mois qu'un allocateur.

    Surcharger new/delete n'est pas sans danger pour un débutant (sans être méchant il a déjà du mal avec les templates) car la syntaxe est particulière et cela impacte tout le programme. Ca reste une solution à proscrire dans la grande majorité des cas, sinon la STL n'aurait pas inventé la notion d'allocateur.

    Et puis t'oublies de mentionner la surcharge des allocateurs new[]/delete[] qui vont généralement de paire avec new/delete

    PS: Et après tout, qui a encore besoin de new/delete? Ca fait belle lurette que je ne les utilise plus, principalement grâce aux containers de la STL et autres encapsulations...

  11. #11
    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 : 41
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Par défaut
    C'est bien beau tout ça, mais c'est plus que compliqué pour faire ce qui n'est ni plus ni mois qu'un allocateur.
    J'ai jamais fait d'allocateur. Je pensais que ça n'avait d'intérêt que via les conteneurs, ici ça donnerait quoi ?

    Surcharger new/delete n'est pas sans danger pour un débutant (sans être méchant il a déjà du mal avec les templates) car la syntaxe est particulière et cela impacte tout le programme
    La syntaxe particulière, quelle qu'elle soit (parce que moi je ne la vois pas), une fois qu'on la connaît et que ça compile ben ma foi... ça tourne.
    Cela n'impacte pas tout le programme, si on utilise une version perso des opérateurs (via un paramètre bidon). C'est comme le new(nothrow) ou le new de placement, on ne les utilise que là où on veut.

    Et puis t'oublies de mentionner la surcharge des allocateurs new[]/delete[] qui vont généralement de paire avec new/delete
    C'est un détail, il suffit de dupliquer les autres en rajoutant des [] dans le nom de l'opérateur.

    PS: Et après tout, qui a encore besoin de new/delete? Ca fait belle lurette que je ne les utilise plus, principalement grâce aux containers de la STL et autres encapsulations...
    On est bien d'accord. Et d'ailleurs à terme cela rend ce genre de stratégie complétement inefficace (et là on peut parler des allocateurs).

  12. #12
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par défaut
    Citation Envoyé par Laurent Gomila
    J'ai jamais fait d'allocateur. Je pensais que ça n'avait d'intérêt que via les conteneurs, ici ça donnerait quoi ?
    Ca donnerait pas grand chose, ça répond juste à son "cahier des charges" avec sa classe CNew qui ressemble sur le principe très fort à un allocateur.
    Un allocateur n'a effectivement pas grand intérêt hors des containers.

    Citation Envoyé par Laurent Gomila
    La syntaxe particulière, quelle qu'elle soit (parce que moi je ne la vois pas), une fois qu'on la connaît et que ça compile ben ma foi... ça tourne.
    Cela n'impacte pas tout le programme, si on utilise une version perso des opérateurs (via un paramètre bidon). C'est comme le new(nothrow) ou le new de placement, on ne les utilise que là où on veut.
    Moi j'ai par contre jamais surchargé new/delete. A supposé que je veuille le faire, il faudrait vraiment que je consulte la doc...

    La seule fois où j'ai suggéré (sans conviction) une telle surcharge, c'était pour un collègue sans rigueur, qui n'arrivait pas à dénicher les fuites mémoires sur un projet après 2 ans d'implémentation...

  13. #13
    Sub
    Sub est déconnecté
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2003
    Messages : 20
    Par défaut
    Euuh oui je me suis trompé le "bon" code est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template<class T> void Truc::CDelete::operator() (T& objectToDelete)
    {
    	mNumberOfDelete++;
    	mNumberOfBytesDeallocated += sizeof(*objectToDelete);
    	delete objectToDelete;
    }
    Comme le dit Charlemagne, je suis loin de maîtriser les templates, mais c'est pour cela que je fais ce genre de chose. La programmation est un loisir pour moi, ce que j'aime c'est tout refaire. Je n'ai jamais fait de vrai programme juste des bouts de code que j'essaie de faire fonctionner.

    Vous avez de la lecture à me conseiller sur les allocateurs ?

  14. #14
    Inactif  
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    743
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2004
    Messages : 743
    Par défaut
    Je comprends très bien ta curiosité. Et j'ai fait aussi pas mal de tests à une époque...

    Citation Envoyé par Sub
    Vous avez de la lecture à me conseiller sur les allocateurs ?
    J'ai appris les allocateurs sur le tas en regardant le code de la STL, mais tout bon livre avec un chapitre suffisamment fourni sur la STL devrait faire l'affaire.
    J'ai bien aimé le livre de Stroustrup.
    Desolé, je connais pas de docs C++ sur internet, mais y'en a sûrement de bonnes.

  15. #15
    Sub
    Sub est déconnecté
    Membre averti
    Profil pro
    Inscrit en
    Mars 2003
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2003
    Messages : 20
    Par défaut
    Ok, ben grande inspiration et immersion dans le code de la STL.

    Question résolue pour moi.

    Eh bien sur, merci à vous, je me coucherais un peu moins con ce soir.

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

Discussions similaires

  1. [XSLT] Débutant erreur sur déclaration template
    Par nagdrir dans le forum XSL/XSLT/XPATH
    Réponses: 5
    Dernier message: 27/08/2007, 16h08
  2. Templates et Operator()
    Par Alp dans le forum C++
    Réponses: 7
    Dernier message: 14/01/2006, 13h41
  3. [Pb de débutant, et encore] surcharge operator =
    Par 10_GOTO_10 dans le forum C++Builder
    Réponses: 5
    Dernier message: 11/01/2006, 02h30
  4. Réponses: 4
    Dernier message: 08/11/2005, 15h10
  5. [Débutant] template <typename T> et main()
    Par reggae dans le forum Langage
    Réponses: 6
    Dernier message: 22/10/2005, 18h57

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