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 :

Pointeurs et références + template et spécialisation


Sujet :

Langage C++

  1. #1
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut Pointeurs et références + template et spécialisation
    Bonjour à tous,

    Je vous pose aujourd'hui une question qui je pense est assez simple, mais comme je ne sais pas quoi taper sur internet je n'ai rien trouvé à ce sujet.

    Imaginons une classe A, contenant un accesseur sur référence constante J'initialise un pointeur sur cette classe puis je déclare un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const int& variable = a.getX()
    Je détruis le pointeur sur la classe A et évidemment la variable référence n'est plus valide. Mon problème est le suivant : comment détecter que r n'est plus valide sans provoquer un crash du programme ?

    Si vous voulez je peux vous expliquer pourquoi je voudrais savoir ça mais je ne le fais pas ici car cela ne me semble pas nécessaire à la résolution du problème.
    J'ajoute également que je préfère l'utilisation de pointeurs permettant beaucoup plus de souplesse car il suffit d'un pour savoir si le pointeur est valide.



    Je poste également un autre problème afin de ne pas trop surcharger le forum même si ce problème n'a pas grand chose à voir avec le précédent.
    Je serai assez bref : imaginons une classe template A :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T>
    class A
    {
    //du code
    }
    Maintenant imaginons une classe B templatée comportant une fonction template dont le paramètre template pourrait être la classe A avec un paramètre template indéterminé (je sais c'est lourd mais comment expliquer ça autrement ).
    Cela donnerait quelque chose comme ça (même si la ça ne compile pas évidemment)
    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
     
    class B
    {
        //du code
     
        template <typename T, typename U>
        void faireqqch(T<U> machin);
    }
     
    template <typename T, typename U>
    void B::faireqqch(T<U> machin)
    {
        //faire qqch
    }
     
    template <>
    void B::faireqqch(A<T> specialisationclasseATemplateEnT)
    {
        //faire qqch
    }
    Sachant que l'action à réaliser dans le cas de la spécialisation de B avec le paramètre A<T> pourrait être la même quelque soit T !

    Merci par avance

  2. #2
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Salut,

    Pour la première question, c'est une question de design, je pense que tu devrais nous montrez ce que tu essayes de faire, pour voir dans quel cas tu te trouves. Néanmoins, une référence constante ne devrait pas être utilisée sur un type primitif car léger à copier et tu gagnes en performance mais sur des objets plus volumineux.

    Pour la seconde question, il s'agit du template de template :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <template<class> class T, class U>
        void faireqqch(T<U> machin);
    Et puis après tu peux spécialiser en fonction de T avec Quelquechose<U>. Mais peut-on savoir ce qui justifie cette opération assez rare en C++ ?

  3. #3
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Tout d'abord merci pour ta réponse

    En ce qui concerne le premier problème je me suis documenté sur l'amitié en c++ et j'ai trouvé un moyen bien meilleur pour effectuer ce que je cherchais, il est vrai que la référence était ici inutile (tout comme le pointeur d'ailleurs).

    Pour mon second problème, je vais essayer de t'expliquer.
    En fait je fais actuellement un minijeu consistant à envoyer des missiles avec une certaine puissance et un certain angle sur la maison adverse tour à tour. Le missile suit une trajectoire réaliste prenant en compte les paramètres physiques qui régissent son mouvement (poids, frottements, vitesse, accélération ...). Entre les maisons des 2 joueurs se trouve un relief créée aléatoirement et interpolé de la manière voulue par l'utilisateur (linéaire, cosinusoidale ou cubique).
    J'essaie je bien coder ce tout petit jeu et pour cela je veux me doter d'un gestionnaire de collisions capable de détecter des collisions entre plusieurs classes héritant d'une classe BoundingBox (abstraite) : SimpleRectBox, SpriteSimpleRectBox (je code le jeu avec la SFML), ComplexRectBox(pour gestion des angles), SpriteComplexRectBox et d'autres encore. Le gestionnaire de collision sera également capable de détecter la collision entre une BoundingBox et une courbe quelconque en calculant notamment des distances aux courbes.

    Voila pour l'exposition de ce que je souhaite faire
    Maintenant voila comment je conçois mon gestionnaire : il s'agirait selon moi d'une méthode templatée (placée dans un namespace éventuellement)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename U, typename V>
    bool IsCollision(U& boundingBox, V& otherBoundingBox)
    Cette méthode serait spécialisée pour chaque couple U,V de boundingBox et retournerait false lorsque le couple ne serait pas spécialisé. Cette méthode serait également autorisée à accéder aux données des différentes BoundingBox (d'où le friend).

    Or certaines BoundingBox sont templatées, notamment parce que le type sf::Rect de la SFML l'est.
    Donc mon gestionnaire de collisions doit prendre en compte ces classes sans leur assigner un paramètre template différent pour chaque spécialisation de la méthode de collisions (ce qui serait terriblement redondant et laid).
    D'où ma deuxième question

    CQFD (j'espère que vous aurez compris mon raisonnement )

    Ce design est-il à revoir ? Si oui n'hésitez pas à m'en indiquer de nouveaux


    Edit : En fait j'aimerais quand même bien savoir pour ma première question car imaginons qu'une classe A comporte une référence vers une autre classe B, et que cette dernière, allouée dynamiquement, est détruite . Comment faire pour interdire l'accès à une méthode de cette classe détruite depuis la classe A qui dispose toujours de sa référence sur la classe B invalide ?

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <>
    void B::faireqqch(A<T> specialisationclasseATemplateEnT)
    {
        //faire qqch
    }
    La spécialisation partielle n'est possible que pour les classes, fonction libres et méthodes peuvent être spécialisées totalement uniquement.

  5. #5
    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,

    Je reviens en vitesse sur l'accesseur...

    Outre le fait que tu as, ici, beaucoup plus facile de renvoyer directement un entier plutôt qu'une référence constante sur un entier, une habitude particulièrement saine à prendre est de déclarer toutes les fonctions membres qui n'ont pas vocation à modifier l'objet au départ duquel elles sont appelées (c'est, entre autres, le cas de tous les accesseurs ) comme étant des fonctions membres constantes.

    Leur déclaration se faisant sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class MaClass
    {
        publc:
            Type /* const & */ doSomething() const;
    }
    Evidemment, si l'implémentation n'est pas inline, elle prendra la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Type /* const & */ MaClass::doSomething() const
    {
        // ce qui doit etre fait
    }
    De cette manière, si, pour une raison ou une autre, tu venais à vouloir faire quelque chose que la constante interdit (il est très facile d'oublier de déclarer le retour comme étant constant alors qu'on déclare que c'est une référence ) le compilateur t'insultera et attendra que tu corriges ton code
    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

  6. #6
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Ok merci j'ai mieux compris l'intérêt des accesseurs référence constante

    Sinon pour mon problème de template et de collision le fait que je ne puisse pas faire de spécification partielle m'empêche donc de faire ça (enfin ça ne m'empêche pas de le faire mais c'est juste que ça ne fonctionnera pas comme je le souhaite) :

    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
     
    //est utilisé par défaut
    template <template<class> class T, template<class> class U, class V, class W>
    bool CollisionManager::IsCollision(T<V>& boundingBox, U<W>& otherBoundingBox)
    {
        std::cout<<"ca passe pas par la spécialisation partielle "<<std::endl;
        return false;
    }
     
    namespace CollisionManager
    {
    //n'est jamais utilisé
    template <class V, class W>
        inline bool CollisionManager::IsCollision(SimpleRectBox<V>& boundingBox, SimpleRectBox<W>& otherBoundingBox)
        {
            std::cout<<"ca passe bien par la spécialisation partielle"<<std::endl;
            return true;
        }
     
    //est utilisé quand c'est possible
    template <>
        inline bool IsCollision(SimpleRectBox<int>& boundingBox, SimpleRectBox<int>& otherBoundingBox)
        {
            std::cout<<"ca passe bien par la spécialisation totale qui n'est pas ce que je cherche"<<std::endl;
            return true;
        }
    }
    Cela m'embête bien car c'est la deuxième fonction qui m'intéresse, je n'ai pas envie de définir IsCollision(SimpleRectBox ..., SimpleRectBox...) pour chaque type possible, comme pour la troisième fonction !

    Est-ce possible de contourner le problème ? Ou bien dois-je revoir mon design ?

  7. #7
    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
    En programmation générique (et, à bien y penser, en programmation orientée objet aussi, d'ailleurs ) , il faut savoir que tout problème peut se résoudre en rajoutant une abstraction supplémentaire

    Si tu veux pouvoir spécialiser le comportement de isCollision, crées une politique et des traits de politique :
    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
    template <typename First, typename Second>
    struct IsCollision;
    template <typename Second>
    struct IsCollision <Rect, Second> // une collision entre un rectangle et n'importe quoi
    {
        bool operator()(SimpleRectBox<Rect> & first, SimpleRectBox<Second> & second)
        {
            /*   ...   */
        }
    };
    struct IsCollision <Triangle, Circle> // une collision entre un Triangle et un cercle
    {
        bool operator()(SimpleRectBox<Rect> & first, SimpleRectBox<Circle> & second)
        {
            /*   ...   */
        }
    };
    /* ... */
    utilisée sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <class V, class W>
        inline bool CollisionManager::IsCollision(SimpleRectBox<V>& boundingBox, SimpleRectBox<W>& otherBoundingBox)
        {
            return IsCollision()(boundingBox, otherBoundingBox);
        }
    j'ai écrit il y a quelques temps maintenant toute une prose dont tu pourrais t'inspirer, même si elle traitait de points et de bipoint, pour expliquer le raisonnement à suivre
    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

  8. #8
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Ahah sacré laïus effectivement

    Merci beaucoup pour cette explication j'avais déjà vu quelques trucs sur les classes de trait et politique sans en comprendre l'utilité, je pense que grâce à ça je vais pouvoir enfin saisir ces concepts

    Je regarderai ton exemple demain à tête reposée car cela me paraît assez compliqué tout de même mais c'est ce qui rend ce langage beau . C'est assez fabuleux quand on pense à toutes les possibilités offertes par ce langage dont on sait qu'on ne pourra jamais toutes les découvrir

  9. #9
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Par contre j'aurais voulu savoir si il était possible via des directives de preprocessing d'indiquer à l'utilisateur (par des warnings) quels couples de collisions ne sont pas encore spécialisés.

    Par exemple, je spécialise la fonction isCollision pour les collisions entre SimpleRectBox et SimpleRectBox ainsi que la fonction pour les collisions entre SimpleSpriteRectBox et SimpleSpriteRectBox : l'appel implicite à la fonction isCollision avec 2 boundingbox de type SimpleRectBox et SimpleSpriteRectBox n'etant pas défini, il retournera donc false, la valeur par défaut, et l'utilisateur ne pourra pas savoir si la fonction appelée était spécialisée.

    A chaque ajout d'une nouvelle spécialisation j'aimerais donc qu'un tableau soit mis a jour au niveau du préprocesseur afin que l'utilisateur sache quelles sont les collisions définies (fonction isCollision spécialisée) et quelles sont celles qui ne le sont pas

    Est-ce possible ? Ou inutile ?

  10. #10
    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
    Tel que le code est présenté ici, tu auras même beaucoup mieux :

    si l'utilisateur essaye d'instancier un type de IsCollision qui n'entre pas dans une des spécialisations (partielle ou totale) fournies, tu auras carrément une erreur de compilation

    C'est pas beau, la vie
    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

  11. #11
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Effectivement ce serait cool mais je n'ai pas très bien saisi comment faire avec ton exemple pour spécialiser pour d'autres choses que des SimpleRectBox, à vrai dire j'ai du mal à comprendre

    J'ai regardé la prose que tu as écrite et qui est très intéressante cependant comme cela ne s'applique pas exactement à mon cas j'ai du mal à saisir le concept, car je ne dispose pas d'assez d'expérience et ces choses sont très nouvelles pour moi

    Pour l'instant voici mon code qui marche pour essayer de faire en sorte que tu comprennes exactement ce que je voudrais afin que tu puisses m'aider comme tu l'as fait jusqu'à maintenant, ce qui est d'ailleurs très sympa

    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
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
     
    #ifndef COLLISIONMANAGER_HPP
    #define COLLISIONMANAGER_HPP
     
    #include "boundingboxincludes.hpp"
    #include <iostream>
     
     
    namespace CollisionManager
    {
        template <typename T, typename U>
        bool IsCollision(const T& boundingBox, const U& otherBoundingBox);
     
        template <template<class> class T, class U, class V>
        bool IsCollision(const T<V>& boundingBox, const U& otherBoundingBox);
     
        template <class T, template<class> class U, class V>
        bool IsCollision(const T& boundingBox, const U<V>& otherBoundingBox);
     
        template <template<class> class T, template<class> class U, class V, class W>
        bool IsCollision(const T<V>& boundingBox, const U<W>& otherBoundingBox);
    }
     
    namespace CollisionManager
    {
        template <typename T, typename U>
        inline bool IsCollision(const T& boundingBox, const U& otherBoundingBox)
        {
            std::cout<<"Warning (1) : collision "<<typeid(T).name()<<" "<<typeid(U).name()<<" not defined"<<std::endl;
            return false;
        }
     
        template <template<class> class T, class U, class V>
        inline bool IsCollision(const T<V>& boundingBox, const U& otherBoundingBox)
        {
            std::cout<<"Warning (2) : collision "<<typeid(T<V>).name()<<" "<<typeid(U).name()<<" not defined"<<std::endl;
            return false;
        }
     
        template <class T, template<class> class U, class V>
        inline bool IsCollision(const T& boundingBox, const U<V>& otherBoundingBox)
        {
            std::cout<<"Warning (3) : collision "<<typeid(T).name()<<" "<<typeid(U<V>).name()<<" not defined"<<std::endl;
            return false;
        }
     
        template <template<class> class T, template<class> class U, class V, class W>
        inline bool IsCollision(const T<V>& boundingBox, const U<W>& otherBoundingBox)
        {
            std::cout<<"Warning (4) : collision "<<typeid(T<V>).name()<<" "<<typeid(U<W>).name()<<" not defined"<<std::endl;
            return false;
        }
     
     
        template <typename U, typename V>
        inline bool IsCollision(const sf::Rect<U> &boundingBox, const sf::Rect<V>& otherBoundingBox)
        {
            if(boundingBox.Left>otherBoundingBox.Right||boundingBox.Right<otherBoundingBox.Left||boundingBox.Top>otherBoundingBox.Bottom||boundingBox.Bottom<otherBoundingBox.Top)
                return false;
            return true;
        }
     
        template <>
        inline bool IsCollision(const SimpleSpriteRectBox& boundingBox, const SimpleSpriteRectBox& otherBoundingBox)
        {
            if(boundingBox.getSprite()==NULL||boundingBox.getSprite()==NULL)
                return false;
            return IsCollision(boundingBox.getSprite()->GetSubRect(),otherBoundingBox.getSprite()->GetSubRect());
        }
     
        template <class V, class W>
        inline bool IsCollision(const SimpleRectBox<V>& boundingBox, const SimpleRectBox<W>& otherBoundingBox)
        {
            return IsCollision(boundingBox.getRect(),otherBoundingBox.getRect());
        }
    }
     
     
    #endif

  12. #12
    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
    Je vais essayer d'expliquer un peu:

    L'un dans l'autre, qu'est ce qu'une collision c'est on va dire (à défaut de meilleur terme), un événement qui survient quand deux objets de type inconnus, et potentiellement différent, se rapprochent au point d'en arriver à se toucher.

    On peut donc créer un foncteur qui prendra les deux objets "de type inconnus" en paramètres et qui s'occupera de vérifier s'il y a bel et bien collision.

    Pour rappel, la forme classique d'un foncteur est de l'ordre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct Functor
    {
        bool operator()(Type const & first, Type const & second) const
        {
            /* logique qui finit par renvoyer "true" ou "false" */
        }
    };
    Mais on a parlé "de type inconnu"... Et ca, ca signifie, en gros, que si l'on ne sait pas encore "ce que l'on va devoir gérer comme donnée" , on a une idée de "comment on va les gérer"...

    Tiens, mais c'est typiquement ce que l'on dit de à quoi sert la programmation générique, ca!!!

    Créons donc un foncteur générique qui se chargera de vérifier si deux objets "de type inconnus et potentiellement différents" entrent en collision

    Commençons par une déclaration anticipée de ce foncteur, qui sera la plus générique possible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template < // c'est une structure template, programmation générique oblige
        typename T1 , // qui travaille avec un premier type d'objet
        typename T2  // et avec un deuxième type d'objet (qui peut très bien être différent du premier)
        >
    struct IsInCollision;
    Voilà, j'ai prévenu le compilateur qu'il existe une structure qui s'appelle IsInCollision et qui prend deux paramètres template différents.

    Tu remarqueras que c'est juste une déclaration anticipée, car je n'ai pas donné de corps à la structure et que je n'ai (surtout) pas déclaré le fameux opérateur () qui en fera un foncteur

    Le fait que je n'aie pas déclaré l'opérateur () dans la structure sera particulièrement utile pour la suite, je reviendrai là dessus plus tard

    Maintenant, on peut spécialiser cette structure "au gré de nos besoins"

    On peut, par exemple, décider de spécialiser complètement cette structure pour deux objets de même type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <>
    struct IsInCollision<TonTypePerso1, TonTypePerso1>
    {
        bool operator()(TonTypePerso1 const & first, TonTypePerso1 const & second) const
        {
            /* la logique adaptée */
        }
    };
    ou pour deux types différents
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <>
    struct IsInCollision<TonTypePerso1, AutreTypePerso>
    {
        bool operator()(TonTypePerso1 const & first, AutreTypePerso const &second) const
        {
            /* la logique adaptée */
        }
    };
    Que l'on peut vouloir transmettre dans un ordre différent (hé... c'est pas pareil )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <>
    struct IsInCollision<TonTypePerso1, AutreTypePerso>
    {
        bool operator()(AutreTypePerso const & first, TonTypePerso1 const & second) const
        {
            /* ici, on va sans doute simplement appeler la version précédente
             * pour éviter de se répéter
            */
            return IsInCollision()(second, first);
        }
    };
    ou que tu peux spécialiser partiellement : l'un des types étant clairement identifié, et l'autre restant générique
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template <typename T1>
    struct IsInCollision<UnTroisiemeTypePerso, T1>
    {
        bool operator()(UnTroisiemeTypePerso const & first, T1 const &second) const
        {
            /* la logique adaptée */
        }
    };
    que tu peux aussi décider de passer dans l'autre ordre, car ce n'est, encore une fois, pas pareil
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template <typename T1>
    struct IsInCollision<UnTroisiemeTypePerso, T1>
    {
        bool operator()(T1 const & first, UnTroisiemeTypePersoconst &second) const
        {
            /* la logique adaptée */
        }
    };
    Il est bien évident que TonTypePerso1, AutreTypePerso et UnTroisiemeTypePerso sont autant de classes que tu as créée, et que tu n'es absolument pas limité au nombre de spécialisations que tu veux introduire.

    Tu peux donc, si tu veux fournir une spécialisation pour chaque paire de types envisageable, te retrouver très rapidement avec un grand nombre de spécialisation

    Le tout étant utilisé "simplement" sous une forme tout à fait générique proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template <typename T1, // on travaille de, nouveau, 
                     typename T2  // avec deux types tout à fait inconnus
                   >
    bool  collisionCheckk(T1 const & first, T2 const & second)
    {
        return IsInCollision()(first, second); // mais on appelle l'opérateur () de la classe que l'on vient de spécialiser
    }
    Le compilateur, en croisant cet appel, va chercher parmis toutes les spécialisations que l'on aura donné pour IsInCollision, celle qui correspond le mieux au types des objets que l'on a passé à collisionCheck...

    C'est à dire que, si on écrit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    TonTypePerso1 obj1;
    /* ... */
    AutreTypePerso obj2
    bool inColision = collisionCheck(obj1, obj2);
    il choisira la spécialisation que l'on a donnée qui ressemble à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <>
    struct IsInCollision<TonTypePerso1, AutreTypePerso>
    {
        bool operator()(TonTypePerso1 const & first, AutreTypePerso const &second) const
        {
            /* la logique adaptée */
        }
    };
    Mais si on essaye de vérifier si un objet de type TonTypePerso1 et un objet de CinquiemeTypePerso entrent en collision, alors que l'on n'a pas fourni la spécialisation "qui va bien", le compilateur va se rabattre sur ... la seule forme qui puisse correspondre parmi celles qu'il connait, à savoir:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template < // c'est une structure template, programmation générique oblige
        typename T1 , // qui travaille avec un premier type d'objet
        typename T2  // et avec un deuxième type d'objet (qui peut très bien être différent du premier)
        >
    struct IsInCollision;
    où T1 représente le type TonTypePerso1 et où T2 représente CinquiemeTypePerso.

    Mais, j'ai attiré ton attention sur le fait que je ne déclarais pas l'opérateur () dans la version non spécialisée, tu te souviens (si, je t'assures, relis quelques lignes plus haut )

    Et c'est là que cela devient intéressant

    En effet, le compilateur ne va pas trouver l'opérateur ()(TonTypePerso1 const &, CinquiemeTypePerso const &) et.. va donc t'insulter en disant qu'il ne le trouve pas

    Le mieux de l'histoire, c'est qui réagira de la sorte directement au moment de la compilation, et que tu n'obtiendras donc même pas d'exécutable avant d'avoir... fourni la spécialisation qui va bien

    C'est y pas beau, la vie
    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

  13. #13
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Magnifique c'est vraiment génial ce que tu fais !
    Ca marche très bien maintenant !

    Merci encore ! (sujet résolu)

  14. #14
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    Juste pour la gouverne de ceux qui tombent sur ce post, il me semble qu'il est faux et dangereux de penser que:
    J'ajoute également que je préfère l'utilisation de pointeurs permettant beaucoup plus de souplesse car il suffit d'un
    pour savoir si le pointeur est valide.
    En effet, si le pointeur a survécu à la destruction du propriétaire de la mémoire référencée, il sera !=NULL mais invalide (dangling pointer), avec des conséquence potentiellement dévastatrices...

  15. #15
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2011
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2011
    Messages : 274
    Par défaut
    Ah oui effectivement je viens de m'en rendre compte il y a 5 minutes cela faisait 1 heure que je cherchais le problème

    Désolé pour cette idée fausse et merci de l'avoir corrigé

  16. #16
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 154
    Billets dans le blog
    4
    Par défaut
    Pour cela j'utilise la macro suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #define Delete(p) delete p; p = NULL;
    #define DeleteArray(p) delete[] p; p = NULL;
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  17. #17
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    @Bousk: ça aide grandement pour la gestion du point de vue du propriétaire de la mémoire, mais ça ne résout pas tout: si ce pointeur a été recopié la copie ne passera pas à nul au moment du delete

  18. #18
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 154
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par therwald Voir le message
    @Bousk: ça aide grandement pour la gestion du point de vue du propriétaire de la mémoire, mais ça ne résout pas tout: si ce pointeur a été recopié la copie ne passera pas à nul au moment du delete
    Si on commence à faire n'importe quoi avec ses pointeurs je me dégage de toute responsabilité.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  19. #19
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Si on commence à faire n'importe quoi avec ses pointeurs je me dégage de toute responsabilité.
    Ça se conçoit...mais un bon moyen de ne pas faire de c... avec des pointeurs est de réserver leur usage à des cas où leur côté casse-g est compensé par une bonne raison de s'en servir...
    Et penser que la valeur NULL garantit contre les problèmes de cycle de vie des objets est amha une dangereuse illusion...

  20. #20
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 154
    Billets dans le blog
    4
    Par défaut
    Non mais si tu espères faire un code parfait en supposant que l'utilisateur fait n'importe quoi, tu n'y arriveras jamais.
    Oui l'utilisation de pointeur nécessite un minimum de cerveau. Libérer un pointeur ne fait que libérer la zone pointée, mais il pointe toujours sur la même zone.
    Par chance on a la valeur spéciale NULL (ou nullptr) qui sert de valeur étalon pour un pointeur non alloué.
    Il convient juste d'initialiser son pointeur à NULL, et de le remettre à NULL une fois qu'on a fini de l'utiliser/libérer.
    Et si l'utilisateur ne comprend rien au pointeur et s'amuse à le copier dans tous les sens pour l'utiliser : qu'il se démerde ! mais c'est son problème et plus le mien.
    Quant au "problème de cycle de vie", c'est au développeur de s'assurer de ce dernier.

    Je ne vais pas faire partir ce fil sur un n-ième "les pointeurs pour les nuls, comment bien les utiliser", si ça t'intéresse il y a moultitudes de sujets similaires ici-même.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Transmission de pointeur par référence
    Par bertry dans le forum Débuter
    Réponses: 3
    Dernier message: 06/10/2008, 17h45
  2. [Débutant] Passage de pointeur et classe template
    Par guigouz dans le forum Langage
    Réponses: 3
    Dernier message: 18/04/2008, 14h58
  3. pointeurs et références quoi choisir ?
    Par damien77 dans le forum C++
    Réponses: 23
    Dernier message: 11/03/2008, 21h17
  4. Pointeur sur référence ?
    Par koala01 dans le forum C++
    Réponses: 24
    Dernier message: 15/01/2008, 17h54
  5. pointeur ou référence
    Par damien77 dans le forum C++
    Réponses: 2
    Dernier message: 23/03/2007, 16h43

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