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 :

Comportement des constructeurs dans une autre methode


Sujet :

Langage C++

  1. #1
    Membre expérimenté Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Par défaut Comportement des constructeurs dans une autre methode
    Bonjour,
    Je me pose la question depuis quelques temps, et je n'ai jamais trouvé de réponse qui me convienne.
    J'ai dans mon code pas mal d'objets avec une méthode virtual void update(). Ces objets sont souvent dérivés, avec leur propre méthode virtual void update(). Ce que j'aimerais faire, c'est faire en sorte que si B dérive de A, appeller B->update() fasse un boulot sur B, et appelle A->update().
    C'est toujours possible de le faire explicitement, mais ce que j'aimerais c'est que le comportement se rapproche de celui des constructeurs, c'est a dire que le concepteur de B n'a pas besoin de penser à updater A, que ca se fasse tout seul. Quelque chose dans le style :
    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
    class A
    {
    public:
       void update()
       {
          //update A
       }
    };
     
    class B : public A
    {
    public:
       void update() //appelle A::update() implicitement
       {
          //Update B
       }
    };
    Déja première question : Existe t il un nom à ce comportement ?
    Deuxième question : Est il possible d'obtenir cet effet en C++ ? Est il possible de le faire dans A, c'est a dire que B n'a rien à rajouter pour que ca marche, ou au pire qu'il ait une erreur à la compilation s'il ne fait pas ce qu'il faut ?

  2. #2
    Membre Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Par défaut
    Il suffit de ne pas "override" la fonction.
    Donc il suffit de ne pas déclarer de fonction avec la même signature.

    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
    #include <iostream>
     
    class A
    {
    public:
    	virtual void update() { std::cout << "Update A" << std::endl; }
    };
     
    class B : public A
    { };
     
    class C : public B
    {
    public:
    	virtual void update() { std::cout << "Update C" << std::endl; }
    }; 
     
    int main()
    {
    	// Allocation dynamique + référence pour avoir du polymorphisme
    	A & a0 = * new A();
    	A & a1 = * new B();
    	A & a2 = * new C();
     
    	a0.update();
    	a1.update();
    	a2.update();
     
    	delete &a0;
    	delete &a1;
    	delete &a2;
     
    	return 0;
    }
    donne cette sortie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $ ./a.out 
    Update A
    Update A
    Update C
    Edit:
    Par contre ça ne fait pas de modication sur B avant. Je ne pense pas que ce soit possible automatiquement. Tu peux faire en sorte que la méthode de A appelle deux fonctions et B n'override que la première.

  3. #3
    Membre expérimenté Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Par défaut
    C'est pas du polymorphisme que je veux faire. Pour reprendre ton exemple :
    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
    class A
    {
    public:
    	virtual void update() { std::cout << "Update A" << std::endl; }
    };
     
    class B : public A
    {
    public:
    	virtual void update() { std::cout << "Update B" << std::endl; }
    };
     
    int main()
    {
    	// Pas besoin de références pour le polymorphisme
    	B* b = new B();
     
    	b->update();
     
    	delete b;
     
    	return 0;
    }
    Et j'aimerais que ca me sorte
    Update A
    Update B
    C'est a dire que B "détecte" que A peut etre updaté et lance la fonction correspondante, ou le contraire. J'imagine bien que c'est pas si simple, et qu'il faille ajouter des macros ou autres, mais j'aimerais que ca soit transparent pour B, ou du moins que ca fasse une erreur de compilation si B ne fait pas ce qu'il faut pour que ca marche.

  4. #4
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Ce que tu veux c'est simplement qu'appeler l'update de B appelle aussi l'update d A pour le même objet?
    Il suffit alors d'avoir ton update de B qui l'implémente:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class B : public A
    {
    public:
    	virtual void update() 
            {
                 A::update(); 
                 std::cout << "Update B" << std::endl; 
             }
    };

    A ma connaissance, il n'y a pas d'autre manière "automatique" de le faire.
    Par contre il y a un moyen de le faire pour s'assurer que l'implémentation de la classe enfant n'écrase pas l'implémentation de la classe parente, en écrivant la classe parente de cette façon:
    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
     
    class A
    {
    public:
    	void update()  // interface publique non virtuelle
            { 
                  std::cout << "Update A" << std::endl; 
                  on_update();
            }
    private:
            virtual void on_update(); // implémentation par défaut
    };
     
    class B : public A
    {
    private:
    	virtual void on_update() { std::cout << "Update B" << std::endl; }
    };
    C'est en fait recommandé de ne pas exposer publiquement les fonctions virtuelles, mais d'avoir des fonctions non-virtuelles publiques qui les appellent.
    De cette manière tu peux facilement changer la façon dont sont appelé les fonctions virtuelles sans avoir à changer l'interface de base.

  5. #5
    Membre expérimenté Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Par défaut
    J'avais déja pensé à ces deux méthodes. Dans la première il est facile d'oublier d'appeller A::update() dans B::update().
    La deuxième, elle marche bien quand il y a une seule classe dérivée. Mais si tu dérives plusieurs fois, tu multiplies les on_update(), et ca devient vite le bordel.

    Voici un des mécanismes auquel j'ai pensé, mais que je ne vois pas trop comment designer :
    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
    class Updatable
    {
    private:
       std::list<methode> m_methodes;
    protected:
       void addMethode(methode* p_methode); //Ajouter dans le list
       void removeMethode(methode* p_methode); //retirer de la list
    public:
       void update(); //appelle toutes les méthodes dans list, en séquence
    }
     
    class A : public Updatable
    {
    public:
       A() { addMethode(doupdate); }
       ~A() { removeMethode(doupdate); }
       void doupdate() { //do stuff }
    };
     
    class B : public A
    {
       B() {addMethode(doupdate); }
       ~B() {removeMethode(doupdate); }
       void doupdate() { //do stuff }
    }
    Le problème c'est que je ne peux pas juste stocker des pointeurs de fonction dans la liste, ils sont tous de type différents car dans une classe différente. De plus, il faudrait idéalement que le concepteur de B n'aie pas à appeller addMethode.

  6. #6
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Bien sur que si tu peux, utilise std::function ou boost::function

  7. #7
    Membre expérimenté Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Par défaut
    Bien vu, je ne savais pas qu'il encapsulait aussi bien les pointeurs vers des fonctions membres. Il y a peut etre quelque chose là, reste à automatiser addMethode pour que B n'ait même pas à y penser.

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Si l'update de A doit se faire à un moment précis (par exemple, avant l'update de la partie B, ou, au contraire, après ), tu peux utiliser ce que l'on appelle le pattern NVI (Non Virtual Interface, ou, si tu préfères, interface non virtuelle ).

    L'idée est de ne pas déclarer la fonction publique comme virtuelle, mais de lui faire appeler une autre fonction, virtuelle celle là, qui sera redéfinie (en cas de besoin) dans les classes dérivées:
    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
     
    class A
    {
        public:
            void update()
            {
                doUpdate();
                // partie commune  
                std::cout<< "update  A"<<std::endl;
            }
        private:
            virtual void doUpdate(){std::cout<<"updating..."<<std::endl;}
    };
    class B : public A
    {
        private:
            virtual void doUpdate(){std::cout<<"update B"<<std::endl;}
    };
    class C : public A
    {
        private:
            virtual void doUpdate(){std::cout<<"update C"<<std::endl;}
    };
    class D : public A
    {
        // pas de redéfinition de virtual void doUpdate
    };
    int main()
    {
        A * ptrA = new A;
        A * ptrB = new B ;
        A * ptrC = new C;
        A * ptrD = new D;
        ptrA->update(); // updating... upadte A
        ptrC->update(); // update B update A
        ptrC->update(); // update C update A
        ptrD->update(); // updating... upadte A
       delete ptrA;
       delete ptrB;
       delete ptrC;
       delete ptrD;
       return 0;
    }
    NOTA:
    Si ta classe de base peut se permettre d'etre une classe abstraite, tu peux parfaitement déclarer la fonction doUpdate comme virtuelle pure, mais tu sera alors obligé de la définir dans toutes les classes concrètes

    Cela foncitonne très bien, à deux exception près : le constructeur et le destructeur.

    En effet, si tu écrit un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    A::A()
    {
        update();
    }
    //ou 
    A::~A()
    {
       update();
    }
    ce sera la version A::doUpate() qui sera effectuée.

    Par contre, si tu l'appelle dans le constructeur ou dans le destructeur des classes dérivées, c'est la version correspondant à la classe dont tu écrit le constructeur (ou le destructeur), ou, à défaut, la version de la classe parent qui sera invoquée, selon le principe immuable qu'une classe ne connait que ses parent et ne sait rien des classes qui dérivent d'elle-même

    Au risque de "violer" le principe XP "DRY" (Don't repeat yourself, si tu dois effectuer des actions dans le constructeur ou dans le destructeur, tu ne peux pas compter sur la virtualité des fonctions au niveau de la classe de base.

    Cependant, tu pourrais très bien envisager d'écrire quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    A::A()
    {
       update(); // A::update + A::doUpdate
    }
    B::B()
    {
        doUpdate(); //B::doUpdate only
    }
    C::C()
    {
        doUpdate(); // C::doUpdate only
    }
    le constructeur de A étant appelé avant le constructeur de B ou celui de C, tes classes B et C seront entièreent mises à jour mise à jour quand meme
    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

  9. #9
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Koala01, j'avais déjà proposé la même solution

  10. #10
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 146
    Billets dans le blog
    4
    Par défaut
    Bonjour à tous,

    Ce que j'aimerais faire, c'est faire en sorte que si B dérive de A, appeller B->update() fasse un boulot sur B, et appelle A->update().
    La deuxième, elle marche bien quand il y a une seule classe dérivée. Mais si tu dérives plusieurs fois, tu multiplies les on_update(), et ca devient vite le bordel.
    Tel que je le comprends, il souhaite que dans cette relation
    A; B : A; C : B;
    l'appel de C::Update entraîne B::Update, lui-même entraînant A::Update
    Et ça, à ma connaissance l'unique moyen de le réaliser c'est de l'écrire et ne pas l'oublier.

    S'il ne souhaite pas aller aussi loin, effectivement le NVI est le plus approprié.
    Sinon, la solution "farfelue" que chaque constructeur fait appel à un AddMethod pour ajouter son propre update à la liste des méthodes à appeler lors d'un doUpdate (ou l'inverse) serait appropriée. Attention alors à l'ordre de dépilage de la liste et d'appel des Update.
    Mais je trouve que c'est se compliquer la tâche pour ne pas avoir à réécrire A::Update() dans son B::Update(), et faire par la même occasion preuve d'un peu de rigueur.
    Dans la première il est facile d'oublier d'appeller A::update() dans B::update().
    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.

  11. #11
    Invité
    Invité(e)
    Par défaut
    slt,

    encore plus farfelu
    Représenter ta classe en tant qu'arbre et un appel sur update recursif.

    Qqch comme
    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 A{
     A(A* aChild):child(aChild){}
     void update(){ //do not override this one
      if(child->hasChild()){
        child->update();
      }
      doUpdate();
     }
     void doUpdate(){
      //do the stuff
     }
     bool hasChild(){
      return A==NULL;
     }
     A* child;
    }
    class B:public A{
     B():A(this){}
    }
    Dans les grandes lignes...

  12. #12
    Membre expérimenté Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Par défaut
    Mouais on est vraiment dans le farfelu la je pense. J'espérais une solution miracle, mais je vais rester sur un bon vieux A::update() appelé dans B::update(), c'est encore ce qu'il y a de plus simple.
    Merci a tous d'y avoir réfléchi en tout cas

Discussions similaires

  1. Creation d'un formulaire permettant d'ajouter des information dans une autre fenêtre
    Par @rno0059 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 1
    Dernier message: 22/06/2007, 07h47
  2. Réponses: 8
    Dernier message: 17/04/2007, 11h35
  3. [Formulaires] Traitement des données dans une autre page...
    Par sekiryou dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 04/03/2006, 09h08
  4. [PHP-JS] Traitement des données dans une autre page...
    Par sekiryou dans le forum Langage
    Réponses: 5
    Dernier message: 04/03/2006, 09h06
  5. Réponses: 4
    Dernier message: 19/09/2005, 15h59

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