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 :

Probl�me avec le Liskov Substitution Principle


Sujet :

C++

Vue hybride

Message pr�c�dent Message pr�c�dent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Mai 2010
    Messages
    30
    D�tails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 30
    Par d�faut Probl�me avec le Liskov Substitution Principle
    Salut � tous.
    J'ai une classe B qui h�rite de A.
    Voici bri�vement le code:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    class A {
       ....
       bool belongsTo(std::vector<A>);
    };
     
    class B :  public A {
    ...
    };
    Now, j'appelle la m�thode "belongsTo" avec B en faisant:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
     
    B bb;
    vector<B> listInstancesB; 
    //initialisation de la liste
    isbbInListInstancesB = bb.belongsTo(listInstancesB);
    Et comme certains pourront l'imaginer, j'ai une erreur de type re�u par la m�thode belongsTo.
    Je sais que je peux juste surcharger cette m�thode r�soudre le probl�me mais
    le code est pratiquement le m�me.
    Ainsi j'aimerais savoir s'il n'y a pas une conception qui pourrait me faire contourner le probl�me
    en �tant fid�le au principe "Don't Repeat Yourself".
    Merci d'avance pour vos r�ponses.

  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,

    Il existe plusieurs solution pour palier � ce probl�me mais �a d�pend un peu de ce que fait ta m�thode belongsTo. D'apr�s son en-t�te je dirais qu'elle regarde si l'objet courant appartient au vecteur pass� en param�tre.

    Si c'est le cas alors tu ne devrais pas en faire une m�thode de classe car le corps de cette m�thode ne va pas utiliser les membres de A ou B, ou en tout cas de mani�re indirecte.

    L'id�e c'est de surcharger l'op�rateur == qui va se charger de faire la comparaison entre deux objets et de laisser l'utilisateur de ta classe chercher par lui m�me si la classe est bien dans le vecteur. Par exemple:

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    std::vector<A> va;
    // Remplir va...
    A a = /*...*/;
    if(std::find(va.begin(), va.end(), a) == va.end())
    // Not inside
    else
    // Inside
    Tu peux encapsuler le tout dans une m�thode template par exemple:

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    template<class T>
    bool belongsTo(const std::vector<T>& vec, const T& value)
    {
      return std::find(vec.begin(), vec.end(), value) == vec.end();
    }
    Tu peux alors utiliser cette m�thode avec des vecteurs de A ou de B ou de C...

    Autre chose, fait attention lorsque tu passes tes arguments au m�thode (tu le passes par valeur, il est pr�f�rable (suivant les cas) de le passer par const ref � voir faq pour + d'info).

    Une derni�re chose, si tu appelles souvent ta m�thode belongsTo et que �a devient un goulot d'�tranglement pour ton application, disons que belongsTo est appel� toute les secondes sur un vecteur d'1 million d'�l�ment, il faudra que tu consid�res d'autres techniques pour ne pas � avoir � parcourir ton vecteur en entier. Mais bon, �a rel�ve de l'optimisation et ne t'en fait pas trop �a maintenant, c'est juste pour que tu le saches.

  3. #3
    Expert �minent
    Avatar de M�dinoc
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 397
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 41
    Localisation : France

    Informations professionnelles :
    Activit� : D�veloppeur informatique
    Secteur : High Tech - �diteur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 397
    Par d�faut
    belongsTo en tant que vecteur de A, �a me para�t suspect. Es-tu s�r que tu ne veux pas un vecteur de pointeurs � la place?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parl� avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

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

    Il faut savoir que le type utilis� pour sp�cialiser intervient dans la d�termination du type d�finitif de celui-ci.

    Ainsi ClasseTemplate<A> est d'un type diff�rent de ClassTemplate<B> et ce, m�me si B h�rite de A.

    Dans ton exemple, vector<B> est donc diff�rent de vector<A>.

    Un autre probl�me vient du fait que tu sembles avoir oubli� que, si A est destin� � servir de classe de base pour B, tes classes A et B ont d'office s�mantique d'entit�, ce qui les rend, par nature non copiables, et que tu aurais au minimum du rendre ta classe A non copiable (je t'expliquerai plus loin comment faire ).

    Cette caract�ristique se r�percutant sur l'ensemble des classes qui pourraient h�riter de A aurait fatalement rendu B non copiable, et t'aurais assez rapidement permis de te rendre compte que tu faisais une erreur en essayant de remplir un tableau de B car, pour placer un objet dans une collection, il faut qu'il soit copiable.

    Notes au passage que la transmission d'arguments � une fonction se fait, par d�faut, par copie et que, si tu veux �viter cette copie (qui risque de prendre �norm�ment de ressources, de temps, en plus de faire que les traitement que tu pourrais appliquer dans ta fonction belongsTo ne se r�percutent pas vers tes objets d'origine), tu devrais passer tes collections d'objets par r�f�rence, �ventuellement constante (si tu ne veux pas que les diff�rents puissent �tre modifi�s dans la fonction appel�e).

    Enfin, pour profiter de la substituabilit� sur tes objets, il faut qu'ils soient manipul�s sous la forme de pointeurs ou sous la forme de r�f�rence.

    Mais on ne peut pas cr�er un tableau de r�f�rence!

    Donc, la solution � ton probl�me consiste � remplir un tableau de pointeurs vers A et � le transmettre � ta fonction belongsTo.

    Evidemment, cela t'obligera � allouer tes objets de mani�re dynamique (� coup de new), et donc � veiller � en lib�rer la m�moire quand tu auras fini de travailler avec eux .

    Au final, la solution consiste en plusieurs points:
    1- rendre syst�matiquement tes classes non copiables (et non affectables).

    Cela se fait facilement en C++11 en d�clarant l'op�rateur d'affectation et le constructeur de copie comme delete, sous la forme de
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    class A{
        public:
            A(A const &) = delete;
            A& operator=(A const &) = delete;
            /* ...*/ 
    };
    Si tu ne disposes pas des opportunit�s offertes par C++11 (ou si ton compilateur ne te permet pas encore de d�finir les fonctions delete), tu peux utiliser la classe boost::noncopyable sous la forme de
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    class A : private boost::noncopyable{
        /*...*/ 
    };
    et, dans le pire des cas, tu peux d�clarer sans les d�finir tes constructeur de copie et op�rateur d'affectation dans l'accessibilit� priv�e sous la forme de
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A{
        /* ... */
        private:
            /* ne SURTOUT PAS fournir d'implémentation pour ces deux 
             * fonctions
             */
            A(A const &);
            A& operator=(A const &);
    };
    Ces trois solutions poursuivent le m�me objectif : obtenir une erreur de compilation si, d'une mani�re ou d'une autre, tu essayes de copier un objet de type A (ou de n'importe quel type d�riv� de A) .
    2- Travailler avec un tableau de pointeurs sur A.

    Comme cela sous entend le plus souvent utiliser l'allocation dynamique de la m�moire, il est pr�f�rable d'utiliser des classes RAIIsantes comme std::unique_ptr si tu disposes de C++11.

    Cela te ferait donc utiliser ta classe vector sous la forme de
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    std::vector <std::unique_ptr<A>> tab;
    /* l'insertion de unique_ptr se faisant par exemple sous la forme de */
    tab.emplace_back(std::unique_ptr<A>(new B));
     
    /* l'accès au pointeur pouvant se faire sous la forme de
    tab[i].get()->someInterestingBehaviour();
    Si tu ne disposes pas de C++11, jettes un oeil du cot� des smart_pointers de boost, ils sont d'une utilisation identique

    En dernier recours, tu pourras toujours utiliser des pointeurs nus, mais il devient assez difficile d'assurer un comportement r�sistant aux exceptions

    3- transmet, syst�matiquement, tes std::vector (et autres collections) sous la forme de r�f�rence � tes fonctions

    Si ta fonction appel�e ne doit pas modifier la collection en question, transmet la par r�f�rence constante.

    4- Penses que, en C++, les fonctions ne sont pas virtuelles par d�faut. Si tu veux qu'une fonction (le destructeur par exemple) puisse adapter son comportement en fonction du type r�el de l'objet � partir duquel elle est appel�e, tu dois la d�clarer comme virtuelle

    Au final, ta classe A ressemblera donc � quelque chose comme (c++11 inside)
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    class A {
       public:
           A(A const &)  = delete;
           A & operator=(A const & ) = delete;
           virtual ~A();
       ....
       bool belongsTo(std::vector<std::unique_ptr<A>> /* const */ &);
    };
    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

  5. #5
    Membre averti
    Inscrit en
    Mai 2010
    Messages
    30
    D�tails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 30
    Par d�faut
    Merci � vous pour vos r�ponses tr�s bien illustr�es e veillez m'excuser pour ma r�ponse tardive.
    Trademark, ta solution me convient parfaitement. En fait j'utilise la fonction find dans l'impl�mentation de belongsTo et de plus je n'utilise pas les membres de A et B dans cette m�thode donc l'externalisation de cette m�thode me semble un bon choix. Concernant le passage par r�f�rence de vector<A>, j'y avais pens� met le compilateur me signale une erreur au niveau de cet bout de code:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    A::belongsTo(vector<A> listA) {
       vector<A>::iterator result = find(listA.begin(), listA.end(), *this);
       return result != listA.end();
    }
    La ligne en rouge est l'endroit o� le compilateur me signale une erreur. Apr�s avoir bien lu "les reproches du compilateur", j'ai mieux compris les propos de koala01 concernant le caract�re non copiable des objets. C'est un concept tr�s important que j'ignorais jusqu' � pr�sent!
    Je crois que je vais utiliser un vector de pointeur de B, car cela acc�l�rera la r�-allocation interne du vector.
    Merci � tous encore pour tous ces "insights", je viens d'apprendre �norm�ment tant en conception OO qu'en subtilit�s du langage C++.

+ R�pondre � la discussion
Cette discussion est r�solue.

Discussions similaires

  1. Probl�me avec la substitution de commandes
    Par essomba84 dans le forum Shell et commandes GNU
    R�ponses: 4
    Dernier message: 31/10/2011, 18h53
  2. VC++ Direct3D8, probl�me avec LPD3DXFONT et LPD3DTEXTURE8
    Par Magus (Dave) dans le forum DirectX
    R�ponses: 3
    Dernier message: 03/08/2002, 11h10
  3. Probl�me avec le type 'Corba::Any_out'
    Par Steven dans le forum CORBA
    R�ponses: 2
    Dernier message: 14/07/2002, 18h48
  4. Probl�me avec la m�moire virtuelle
    Par Anonymous dans le forum CORBA
    R�ponses: 13
    Dernier message: 16/04/2002, 16h10

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