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 :

[POO] Affecter a une variable de classe abstraite un objet


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    123
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 123
    Points : 85
    Points
    85
    Par défaut [POO] Affecter a une variable de classe abstraite un objet
    Bonjour

    Je débute en C++ et souhaite affecter à une variable de classe abstraite un objet héritant de cette classe.

    Par exemple:

    J'ai la classe abstraite suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #ifndef OBSERVER_H
    #define OBSERVER_H
     
    class Observer 
    {
    	public:
    		virtual void paint() = 0;
    };
    #endif
    Et le code suivant:

    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
     
    #ifndef BALL_H
    #define BALL_H
     
    #include <vector>
    #include "Observer.h"
     
    class Ball
    {
    	private:
     
    	std::vector<Observer> listObserver;
     
    	public:
     
    	Ball();
     
    	void addObserveurs(Observer observer);
    };
     
    #endif
    Je souhaite que mon vecteur contienne une suite d'objets héritant de Observer or quand je compile j'ai le message d'erreur suivant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     cannot declare parameter «observer» to be of abstract type «Observer»
    IHM/../Model/Observer.h:5: note:   because the following virtual functions are pure within «Observer»:
    Il y'a t'il un moyen pour que je puisse conserver ma classe abstraite?

    Merci d'avances de vos réponses

  2. #2
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Bonjour

    je ne suis pas sûr d'avoir bien compris ta question, donc je m'excuse par avance si je répond à côté.

    Une classe virtuelle ne peut pas être instanciée. Donc dans ton exemple, il est impossible de créer une instance (un objet) de type Observer.

    La solutoin est de faire un tableau de pointeur de Observer. 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
    class Obs1 : public Observer // classe qui hérite de Observer
    // code de la classe
    };
     
    class Obs2 : public Observer // autre classe qui hérite de Observer
    {
    // code de la classe
    };
     
    // ailleur dans ton programme:
    std::vector<Observer*> listObserver;
    listObserver.push_back( new Obs1() ); // ici on crée un objet de type Obs1, le new renvoie un pointeur, et on stocke ce pointeur dans le vecteur
    listObserver.push_back( new Obs2() ); // ici on crée un objet de type Obs2, le new renvoie un pointeur, et on stocke ce pointeur dans le vecteur
    Il est à noter que cette méthode est dangereuse car il ne faut pas oublier de détruire les objets du vecteur "à la main" avant de détuire ce vecteur.

    Une autre méthode consiste à passer par une classe tierce qui contiendra un pointeur vers un Observer, mais je ne suis pas persuadé que ce soit réellement mieux. Je ne sais pas.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  3. #3
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Bonjour,

    Lorsque tu écris ceci :
    cela va automatiquement instancier la classe Observer (contrairement à ce qui se passe en Java).

    Il faut que tu utilises un pointeur pour utiliser la notion de polymorphisme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Observer* o;
    ConcreteObserver concrete_o;
    o = &concrete_o;
    ou encore :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Observer* o = new ConcreteObserver();
    Par conséquent, lorsque tu fais ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    void addObserveurs(Observer observer);
    tu vas forcément instancier la classe Observer, ce qui n'est pas possible car comme tu le sais cette classe est abstraite.

    Il faut donc que ta fonction prenne un pointeur d'Observer ou de préférence une référence d'Observer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    void addObserveurs(Observer& observer);
    Même chose ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    std::vector<Observer> listObserver;
    Il faut plutôt que tu écrives cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    std::vector<Observer*> listObserver;
    car autrement, même lorsque tu penseras ajouter à ton vecteur un objet de type ConcreteObserver, celui-ci sera converti en Observer. Si ton vecteur stocke des pointeurs, les objets pointés ne seront ni copiés, ni convertis.


    À noter que le design pattern Observer est implémenté (entre autres) dans la lib Boost.Signal.
    Mais c'est effectivement une bonne chose de coder le pattern à la main pour bien comprendre son mécanisme .
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    123
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 123
    Points : 85
    Points
    85
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    std::vector<Observer*> listObserver;
    Ok merci de m'avoir répondu

    Le code marche si j'utilise une liste de pointeur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	std::vector<Observer*> listObserver;
     
    	void addObserveurs(Observer* observer);
    Le truc que je ne comprend pas c'est la partie de ta phrase "lorsque tu penseras ajouter à ton vecteur un objet de type ConcreteObserver, celui-ci sera converti en Observer".
    Ce que je comprend c'est que si je passe une référence de ConcreteObserver il va me faire un cast en Observer et donc planter?

    Et une dernière question pour réagir à "Il est à noter que cette méthode est dangereuse car il ne faut pas oublier de détruire les objets du vecteur "à la main" avant de détuire ce vecteur."

    Si dans mon vecteur j'enregistre des pointeurs d'objets alloués non dynamiquement:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        ConcretObserver c = ConcretObserver();   // Classe implémentant observeur
        Ball b = Ball();
        b.addObserveurs(&c);
    }
    En théorie je n'ai pas besoins de parcourir la liste avant la suppression de l'objet ball?

    Merci de vos réponses promptes et rapides.

  5. #5
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Citation Envoyé par Anonymouse Voir le message
    Le truc que je ne comprend pas c'est la partie de ta phrase "lorsque tu penseras ajouter à ton vecteur un objet de type ConcreteObserver, celui-ci sera converti en Observer".
    Ce que je comprend c'est que si je passe une référence de ConcreteObserver il va me faire un cast en Observer et donc planter?
    Dans ton cas précis, vu que Observer est abstrait, c'est pas que ça va planter, c'est surtout que ça va pas compiler du tout !
    Mais sinon oui, c'est tout à fait ça.

    Citation Envoyé par Anonymouse Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        ConcretObserver c = ConcretObserver();   // Classe implémentant observeur
        Ball b = Ball();
        b.addObserveurs(&c);
    }
    Le code que tu nous montres est équivalent à celui-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    int main(void)
    {
        ConcretObserver temp_observer;
        ConcretObserver c = temp_observer;   // Classe implémentant observeur
        Ball temp_ball;
        Ball b = temp_ball;
        b.addObserveurs(&c);
    }
    ce qui est donc strictement équivalent à ceci, sachant que les variables temporaires ne sont pas utilisées :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        ConcretObserver c;   // Classe implémentant observeur
        Ball b;
        b.addObserveurs(&c);
    }
    Cela va planter à coup sûr si tu fais ce genre de choses dans une fonction, car l'objet c va être détruit dès la fin de la portée (c'est-à-dire le premier "}" rencontré). Le pointeur ainsi enregistré dans ton vecteur sera donc invalide (i.e. pointe sur un objet détruit).

    Voilà ce que tu dois faire pour que c ne soit pas détruit à la fin de la portée : utiliser la création dynamique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        Observer* c = new ConcreteObserver();   // Classe implémentant observeur
        Ball b;
        b.addObserveurs(c);
    }
    Ce que t'as dit r0d, concrètement, c'est qu'il ne faudra pas oublier de détruire (avec delete) l'objet pointé par c au moment où tu voudras enlever cet objet du vecteur d'observeurs. Si tu ne le fais pas, c'est la fuite mémoire : un objet alloué dynamiquement devient inaccessible (plus aucun pointeur ne pointe dessus) sans avoir été détruit.


    Vu les questions que tu poses, je suis quasi-persuadé que tu as appris la POO avec le Java. Si c'est bien le cas, je te conseille vivement de lire un cours sur le C++ en mettant de côté ce que tu sais sur le Java.
    Notamment, on écrit pas « Ball b = Ball(); » !
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    123
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 123
    Points : 85
    Points
    85
    Par défaut
    Citation Envoyé par Florian Goo Voir le message
    Dans ton cas précis, vu que Observer est abstrait, c'est pas que ça va planter, c'est surtout que ça va pas compiler du tout !
    Mais sinon oui, c'est tout à fait ça.


    Le code que tu nous montres est équivalent à celui-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    int main(void)
    {
        ConcretObserver temp_observer;
        ConcretObserver c = temp_observer;   // Classe implémentant observeur
        Ball temp_ball;
        Ball b = temp_ball;
        b.addObserveurs(&c);
    }
    ce qui est donc strictement équivalent à ceci, sachant que les variables temporaires ne sont pas utilisées :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        ConcretObserver c;   // Classe implémentant observeur
        Ball b;
        b.addObserveurs(&c);
    }
    Cela va planter à coup sûr si tu fais ce genre de choses dans une fonction, car l'objet c va être détruit dès la fin de la portée (c'est-à-dire le premier "}" rencontré). Le pointeur ainsi enregistré dans ton vecteur sera donc invalide (i.e. pointe sur un objet détruit).

    Voilà ce que tu dois faire pour que c ne soit pas détruit à la fin de la portée : utiliser la création dynamique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    {
        Observer c* = new ConcreteObserver();   // Classe implémentant observeur
        Ball b;
        b.addObserveurs(c);
    }
    Ce que t'as dit r0d, concrètement, c'est qu'il ne faudra pas oublier de détruire (avec delete) l'objet pointé par c au moment où tu voudras enlever cet objet du vecteur d'observeurs. Si tu ne le fais pas, c'est la fuite mémoire : un objet alloué dynamiquement devient inaccessible (plus aucun pointeur ne pointe dessus) sans avoir été détruit.


    Vu les questions que tu poses, je suis quasi-persuadé que tu as appris la POO avec le Java. Si c'est bien le cas, je te conseille vivement de lire un cours sur le C++ en mettant de côté ce que tu sais sur le Java.
    Notamment, on écrit pas « Ball b = Ball(); » !
    Gagné j'ai bien appris la POO avec Java .

    Et un des premiers trucs que j'ai lu c'est: "les programmeurs java ils ne font que des news" donc j'essaye de ne pas tomber dans l'allocation dynamique à tord et à travers .

    Je vais clairement bucher les cours de C++ du site pour ne pas faire du Java en C++.

    Merci de votre aide

  7. #7
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Whoops, faute de frappe, l'étoile c'est après le type, pas après le nom de la variable :
    Observer* c = new ConcreteObserver();
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  8. #8
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Florian Goo Voir le message
    Ce que t'as dit r0d, concrètement, c'est qu'il ne faudra pas oublier de détruire (avec delete) l'objet pointé par c au moment où tu voudras enlever cet objet du vecteur d'observeurs. Si tu ne le fais pas, c'est la fuite mémoire : un objet alloué dynamiquement devient inaccessible (plus aucun pointeur ne pointe dessus) sans avoir été détruit.
    Effectivement. Merci pour la précision
    Pour détruire proprement un conteneur de pointeurs, voir, par exemple, cette faq.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

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

Discussions similaires

  1. Réponses: 4
    Dernier message: 14/10/2009, 10h52
  2. Réponses: 4
    Dernier message: 25/06/2009, 15h14
  3. [POO] [Orienté objet] Affectation d'une variable
    Par luc2verga dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 29/02/2008, 02h34
  4. [POO] Prob avec une méthode de classe
    Par Ludo75 dans le forum Langage
    Réponses: 9
    Dernier message: 06/02/2006, 22h37
  5. Réponses: 3
    Dernier message: 12/10/2005, 09h23

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