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 :

Classe abstraite et methode static de factory


Sujet :

C++

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 208
    Points : 136
    Points
    136
    Par défaut Classe abstraite et methode static de factory
    Bonjour,

    J'aimerai créer une classe abstraite A et deux classe dérivante AA et AB
    Je veux que la classe A contienne une méthode static pour la création des objets de type AA et AB et une méthode virtuel implémenté dans AA et AB

    J'ai donc le code suivant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A
    {
    public:
    	static A Create(BOOL bIsB = false);
    	~A(void);
    protected:
    	A(void);
    	virtual void Run()=0;
    };
    Pour AA et AB
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class AA :
    	public A
    {
    public:
    	AA(void);
    	~AA(void);
    	virtual void Run();
    };

    Pour la méthode Create j'ai:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    A A::Create(BOOL bIsB)
    {
    	if(bIsB)
    		return AB();
    	else
    		return AA();
    }

    Et à la compilation j'ai une erreur : error C2259: 'A'*: impossible d'instancier une classe abstraite


    J'ai loupé quelque chose???

    Merci pour vos lumières

  2. #2
    Membre éclairé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 426
    Points : 827
    Points
    827
    Par défaut
    Salut,
    J'ai loupé quelque chose???
    Oui en effet! La classe A est une classe abstraite (c'est même toi qui le dit ) alors
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    static A Create(BOOL bIsB = false);
    ...
    A A::Create(BOOL bIsB)
    n'est pas correct, car 'Create' renvoie un objet de la classe A : Comme A est une classe abstraite, on ne peut pas créer d'objets de cette classe. C'est d’ailleurs ce que dit ton message d'erreur.

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 208
    Points : 136
    Points
    136
    Par défaut
    Oui mais je crée pas réellement un objet de type A, je crée un objet de type AB ou AA et je renvoie un objet de type A.

    En c#, cela est possible. Masi en C++ je dois apparemment renvoyé un pointeur de A :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    A* A::Create(BOOL bIsB)
    {
    	if(bIsB)
    		return new AB();
    	else
    		return new AA();
    }

  4. #4
    Membre éclairé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 426
    Points : 827
    Points
    827
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    je crée un objet de type AB ou AA et je renvoie un objet de type A.
    Non, non et non on ne peut pas!!!

    Les AA et les AB tu en instancie tant que tu veux, mais pour les A ce n'est pas possible, ou sinon, il faut que A ne soit pas abstraite

    De plus, si A n'était pas abstraite, tu pourrais être confronté au problème de 'slicing' développé ici

  5. #5
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 208
    Points : 136
    Points
    136
    Par défaut
    Citation Envoyé par bertry Voir le message
    Les AA et les AB tu en instancie tant que tu veux, mais pour les A ce n'est pas possible
    Argf, justement je n'instancie pas de A: tu as vu ça où???

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par Troopers Voir le message
    Argf, justement je n'instancie pas de A: tu as vu ça où???
    Mais si, justement...

    Comme tu ne renvoie ni un pointeur ni une référence, ce que tu renvoie est... une instance de type A (et non de type AA ou de type AB), ce qui est strictement interdit...

    Pour que cela fonctionne, et pour profiter du polymorphisme au passage, tu dois renvoyer une référence ou un pointeur sur un objet "passant pour être" de type A.

    Tu dois, en outre, faire attention au fait que l'objet référencé ne soit pas détruit au moment de sortir de la fonction, si tu décide de renvoyer une référence.

    La signature de ta fonction doit donc être de l'ordre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class A
    {
    public:
    	static A& Create(BOOL bIsB = false);
    };
    ou de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class A
    {
    public:
    	static A Create(BOOL bIsB = false);
    };
    et son implémentation proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    A* A::Create(BOOL bIsB)
    {
    	if(bIsB)
    		return new AB();
    	else
    		return new AA();
    }
    ou, si cela a un sens, t'orienter vers quelque chose de proche de
    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:
        static A& Create(BOOL bIsB = false)
        {
            if(! m_member)
            {
                if(bIsB)
                    m_member = new AB();
                else
                   m_member = new AA();
                }
            return *m_member;
        }
        private:
        A* m_member;
    };
    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

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 208
    Points : 136
    Points
    136
    Par défaut
    Ok! merci pour l'explication

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Par défaut, en C++, les objets sont manipulés par valeur et non par référence/pointeur. En C#, c'est le contraire, par défaut, les variables manipulent les objets par références. D'où cette confusion.

  9. #9
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 208
    Points : 136
    Points
    136
    Par défaut
    Autre question:
    si je veux que la fonction Create renvoie une référence :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    A& A::Create(BOOL isB)
    {
    	A* pA = NULL;
    	if(isB)
    		pA = new AB();
    	else
    		pA= new AA();
    	return *pA;
    }
    Comment savoir si la référence est initialisée et comment l'initialiser par défaut :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    A& a; // Nécessite une initialisation par défaut?
    if(!a)// si a n'est pas initialisé?
    {
    	a = A::Create(true);
    }
    else
    {
    	a.Run();		
    }
    Et faut -il détruire l'objet ou bien il est automatiquement détruit à la fin de la portée?

  10. #10
    Membre éclairé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 426
    Points : 827
    Points
    827
    Par défaut
    Il serait plus simple d'utiliser la fonction de Koala01 renvoyant un pointeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    A* A::Create(BOOL bIsB)
    {
    	if(bIsB)
    		return new AB();
    	else
    		return new AA();
    }
    C'est plus simple à manipuler.

    Citation Envoyé par Troopers Voir le message
    Et faut -il détruire l'objet ou bien il est automatiquement détruit à la fin de la portée?
    Si tu le crée un objet avec new, tu dois le détruire toi-même avec delete

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Il s'agit d'être particulièrement prudent lorsque tu envisages de renvoyer des objets polymorphes à créer sous la forme de références...

    Il faut, en effet, veiller à ne pas perdre l'adresse à laquelle ces objets polymorphes sont placés, et nous serions donc, peu ou prou, dans une situation de "gestionnaire", car on peut envisager de distinguer les responsabilités:
    • La responsabilité de décision quant au type réel de l'objet à créer d'un coté et
    • La responsabilité de maintenir quelque part l'ensemble des objets créés afin de pouvoir y accéder par la suite (et de les détruires lorsqu'ils ne sont plus nécessaires) d'autre part

    Cependant, différentes situations peuvent apparaitre, selon que tu es en mesure de déterminer clairement qui décidera de détruire les objets une fois qu'ils ne sont plus nécessaire, ou non.

    Tu pourrais, ainsi, avoir effectivement une classe "manager" proche de:
    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
    class Base;
    /* je considère que la classe Base dispose d'une méthode id() renvoyant un
     * un "size_t" permettant d'identifier chaque objet de manière unique et non
     * ambigue ;-)
     */
    class Manager
    {
         /* pour la facilité, à usage interne uniquement */
         typedef std::map<int, Base*> BaseMap;
         typedef typename BaseMap::iterator iterator;
         typedef typename BaseMap::const_iterator const_iterator;
         public:
             void registerItem(Base * toadd)
             {
                 items_.insert(std::make_pair(toadd->id(),toadd));
             }
             Base & find(size_t id)
             {
                  iterator it = items_.find(id);
                  assert(it!=items_.end());
                  return  *(it.second);
             }
             void unregisterAndDestroy(Base* b)
             {
                 unregisterAndDestroy(b->id());
             }
            void unregisterAndDestroy(size_t id)
            {
                iterator it = items_.find(id);
                assert(it != items_.end());
                delete it.second;
                items_.erase(it);
            }
            void clearAllItems()
            {
                 for(iterator it = items_.begin(); it!=items_.end();++it)
                     delete it.second;
                 items_.clear();
            }
            size_t itemsCount() const{return items_.size();}
        private:
            static BaseMap items_;
    };
    // ne pas oublier l'initialisation de items_ dans un *.cpp
    Manager::BaseMap Manager::items = Manager::BaseMap();
    et ta "fabrique" pourrait ressembler à
    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 Fabrique
    {
         Base & create(bool isB)
         {
             Manager man;
             size_t id = man.itemsCount();
             A * createdObject = 0;
             if(isB)
             {
                 createdObject = new AB(id);
             }
             else
             {
                 createdObject = new AA(id);
             }
             if(createdObject)
             {
                 man.registerItem(createdObject );
                 return man.find(id);
             }
             throw std::bad_alloc();
         }
    };
    Tu remarquera que je n'ai pas utiliser le type Base comme fabrique, simplement parce qu'il me semble important de clairement séparer les différentes responsabilités : Le type de base a déjà une résponsabilité bien à lui : permettre d'identifier les différentes instances de manière unique et non ambigüe et, accessoirement, fournir l'interface commune aux différents types. Il ne semble donc pas opportun d'y rajouter une responsabilité qui serait de créer de nouvelles instances, et encore moins que ces nouvelles instances soient de type dérivés

    Une fois les objets créés, tu pourras, bien sur, utiliser la référence sur le nouvel objet renvoyée par la fonction Fabrique::create(), mais tu pourra également, en cas de besoin, récupérer n'importe quel objet créé par ailleurs avec un simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Manager man;
    Base & laRef = man.find(une_id_donnee);
    (pour autant que tu disposes de l'id de l'élément qui t'intéresse, bien sur)

    L'avantage étant que, comme tu travaille avec des références, tu évite la tentation à l'utilisateur d'invoquer un delete sur les pointeurs, étant donné qu'il n'a absolument pas à le faire, vu que la durée de vie des éléments est gérée au travers... de la classe Manager (et de ses fonctions membres)
    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

  12. #12
    Membre habitué
    Profil pro
    Inscrit en
    Mai 2002
    Messages
    208
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2002
    Messages : 208
    Points : 136
    Points
    136
    Par défaut
    Ok,
    Merci pour l'exemple complet et les explications

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

Discussions similaires

  1. [POO] classe abstraite sans methode
    Par ZaaN dans le forum C++
    Réponses: 7
    Dernier message: 01/05/2007, 19h21
  2. Classe interne, methode static et ajout de boutons
    Par comme de bien entendu dans le forum AWT/Swing
    Réponses: 6
    Dernier message: 09/03/2006, 14h13
  3. [POO] class abstraite et methode magic
    Par jeff_! dans le forum Langage
    Réponses: 14
    Dernier message: 24/01/2006, 23h19
  4. Réponses: 13
    Dernier message: 15/11/2005, 15h47
  5. Réponses: 6
    Dernier message: 27/07/2005, 09h06

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