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

Boost C++ Discussion :

Héritage et petite question sur boost::shared_ptr


Sujet :

Boost C++

  1. #1
    Membre habitué
    Inscrit en
    Octobre 2004
    Messages
    616
    Détails du profil
    Informations forums :
    Inscription : Octobre 2004
    Messages : 616
    Points : 164
    Points
    164
    Par défaut Héritage et petite question sur boost::shared_ptr
    Bonjour bonjour !
    Deux petit problème s'offrent à moi :

    J'ai une classe ( Kernel ) qui contient une map de "string <-> *Task"
    Chaque Task a donc un identifiant précis qui me permet d'y acceder .

    La classe Task est virtuelle non instanciable ( 3 fonction virtuelle pure ) .

    Ma classe Kernel fournit une methode Task * GetTask(string) qui permet de renvoyé un pointeur sur la tache en question .

    1ere problème :

    Comment renvoyez un pointeur sur le ype dérivée et non sur la classe mére ?
    Pexemple sur une
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class Derived : public Task 
    {};
    ou sur une
     class Derived2 : public Task 
    {};
    De facons à accéder aux methode aprticulière de chacune des Task filles.

    2eme petit problème : J'aimerai que ma fonction renvoie un pointeur null ou equivalent sous boost si la task demandé n'est pas présente dans le Kernel . J'ai pour le moment fait comme suis, mais je ne suis pas convaincu du résultat . Il y d'ailleur surement un deisgn plus élégant au niveau de la gestion d'erreur de ce type, je suis ouvert a toute proposition . (idem concernant mon design de départ !!)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    		private:
     
    			std::map<std::string, boost::shared_ptr<ITask> > Tasks_ ;
    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
     
    // Return a pointer to the requested task
    boost::shared_ptr<ITask> Kernel::GetTaskByName(const std::string & TaskToGet) const
    {
    	std::map<std::string, boost::shared_ptr<ITask> >::const_iterator It = Tasks_.find(TaskToGet);
     
    	if ( It == Tasks_.end() )
    	{
    		Log(LOG_ALL,LOG_WARNING) << " Task : " << TaskToGet << " not found in the Kernel" ;
     
    		boost::shared_ptr<ITask> Tmp ;
    		return Tmp ;
    	}
     
    	return It->second ;
    }
    Merci !

  2. #2
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Salut,

    Pour ta première question, tu n'as pas trop le choix :
    - soit tu implémentes deux fonctions, chacune te renvoyant un type de pointeur différent, l'intérêt de l'héritage se limitant alors à la factorisation du code commun et au stockage de tes objets de manière uniforme.
    - soit tu passes par du dynamic_cast ou le pattern visiteur pour accéder aux méthodes particulières.

    Pour le deuxième point, apparemment tu ne retires pas ls tasks de ton vecteur contenu dans kernel, j'en déduis donc (peut-être à tord) que c'est lui qui a l'ownership de tes tasks. Dans ce cas-là pourquoi passer par des shared_ptr ? Ceux-ci sont à utiliser quand la propriété d'ownership est réellement partagée, et que donc c'est le dernier qui l'utilise qui libère la mémoire allouée. Si ce n'est pas le cas (par exemple ce n'est que ta classe Kernel qui détruit tes pointeurs sur tasks), aucun besoin d'utiliser des shared_ptr.

  3. #3
    Membre habitué
    Inscrit en
    Octobre 2004
    Messages
    616
    Détails du profil
    Informations forums :
    Inscription : Octobre 2004
    Messages : 616
    Points : 164
    Points
    164
    Par défaut
    Merci de ta réponse
    Pour le premier cas, effectivement je me souviens qu'il est possible de passer par une fonction template à base de return dynamic_cast du type template . Mais je trouvais ca assez moche ...enfin pourquoi pas, j'espèrais en fait trouver un design général +élégant

    Pour le deuxième point tu suggère du mettre des ITASK* simplement dans ma map et de return NULL ; dans la fonction lorsque l'on demande une task non présente?
    Ca va marcher je supose, mais ca induit deux problèmes :
    - ca me force a gérer la mémoire moi-même .
    - j'ai bien une fonction qui permet de retirer des tasks de mon kernel

    Concernant l'ownership, j'ai encore de gros doute, je ne vois aps trés bien comment je vais m'en sortir élégament en dehors du kernel .

    Dans les classes ayants besoin d'un pointeur vers une tache précicse j'ai deux solutions :

    - soit je déclare en menbre un pointeur que je récupère une fois dés que possible ( plus léger mais ca me fait un Xeme pointeur sur ma donné donc je doit garder des shared_ptr )
    - soit je recupère un pointeur temporaire a chaque fois que j'en ai besoin ( plus lourd mais plus d'ownership qui pose problème, je peux alors utiliser des scoped_ptr )

    Je vais surement reposter un topic plus complet, histoire de discuter de la valitidé de mon design qui se retrouve toujours et encore avec le même problème ... des modules séparés capable de traitements spécifiques mais ayant besoin de données contenu autres part et je ne trouve jamais un bon moyen de communication inter-taks .

    ( l'exemple le plus simple : la tache Render(), capable d'afficher un sprite ... mais si personne ne lui passe un objet Sprite, elle ne va pas servir a grand chose ... )

    Désolé pour le pavé : >

  4. #4
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    Pour le premier cas, effectivement je me souviens qu'il est possible de passer par une fonction template à base de return dynamic_cast du type template . Mais je trouvais ca assez moche ...enfin pourquoi pas, j'espèrais en fait trouver un design général +élégant
    Il faut commencer par savoir pourquoi tu stockes des pointeurs sur classe de base, tout en voulant les récupérer avec leur type réel. Soit tu ne voulais en fait pas amalgammer toutes tes instances dans le même panier, soit le (ou les) cas où tu as besoin du type réel de ta tâche peut en fait utiliser la virtualité.

    Concernant l'ownership, j'ai encore de gros doute, je ne vois aps trés bien comment je vais m'en sortir élégament en dehors du kernel
    Si c'est le kernel qui gère la durée de vie des tâches, pourquoi voudrais-tu les englober dans des xxx_ptr en dehors ? Aucun besoin de scoped_ptr ni de shared_ptr. A moins que ce soient les classes qui les utilisent qui doivent les détruire.

  5. #5
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Citation Envoyé par Clad3
    Merci de ta réponse
    Pour le premier cas, effectivement je me souviens qu'il est possible de passer par une fonction template à base de return dynamic_cast du type template . Mais je trouvais ca assez moche ...enfin pourquoi pas, j'espèrais en fait trouver un design général +élégant
    Comme je l'ai dit dans mon précédent message (et dans le cas où les cas cités par Laurent Gomilla ne sont pas applicables, donc à examiner en premier quand même avant de se tourner vers la solution que je te propose), tu peux regarder du côté du Design Pattern Visiteur.

    Pour le deuxième point tu suggère du mettre des ITASK* simplement dans ma map et de return NULL ; dans la fonction lorsque l'on demande une task non présente?
    Ca va marcher je supose, mais ca induit deux problèmes :
    - ca me force a gérer la mémoire moi-même .
    - j'ai bien une fonction qui permet de retirer des tasks de mon kernel
    Si chacune de tes taches est détruite par le kernel lorsqu'elle est retirée de celui-ci, cela ne devrait pas poser un énorme problème.

    Concernant l'ownership, j'ai encore de gros doute, je ne vois aps trés bien comment je vais m'en sortir élégament en dehors du kernel .

    Dans les classes ayants besoin d'un pointeur vers une tache précicse j'ai deux solutions :

    - soit je déclare en menbre un pointeur que je récupère une fois dés que possible ( plus léger mais ca me fait un Xeme pointeur sur ma donné donc je doit garder des shared_ptr )
    Encore une fois, non, ce n'est pas parce que tu as plusieurs pointeurs sur une même donnée qu'il te faut obligatoirement utiliser les shared_ptr. De ce que tu dis, je comprends que tu souhaites que le Kernel ait l'ownership de tes Tasks. Donc tu peux avoir autant de pointers que tu veux sur tes tâches, la seule chose à faire est de s'assurer qu'aucun delete n'est appelé sur ceux-ci en dehors de ta classe Kernel.

    - soit je recupère un pointeur temporaire a chaque fois que j'en ai besoin ( plus lourd mais plus d'ownership qui pose problème, je peux alors utiliser des scoped_ptr )
    Cf mon commentaire du dessus, tu peux très bien récupérer le pointer dès le début.

  6. #6
    Membre habitué
    Inscrit en
    Octobre 2004
    Messages
    616
    Détails du profil
    Informations forums :
    Inscription : Octobre 2004
    Messages : 616
    Points : 164
    Points
    164
    Par défaut
    Il faut commencer par savoir pourquoi tu stockes des pointeurs sur classe de base, tout en voulant les récupérer avec leur type réel. Soit tu ne voulais en fait pas amalgammer toutes tes instances dans le même panier, soit le (ou les) cas où tu as besoin du type réel de ta tâche peut en fait utiliser la virtualité
    Je stocke toute mes classes dans un meme conteneur, pour cela j'ai besoin d'une classe de base .
    Ensuite chacune de mes classes dérivée va implémenter 3 methodes communes( dont la principale étant une fonction Update() ) . Ces 3 méthode sont virtuelle pure car leur comportement est trés différent selon la classe dérivée .
    On a également 2 méthode virutuel, non pure, car le comportement de la classe de base ar défaut est acceptable dans un cas général .... au cas par cas on epxu toujours redéfinir cette methode si besoin dans la classe fille.
    Enfin chacune de ces classes dérivées devra réaliser des taches bien différentes et pour cela posséder des méthodes particulières ( LoadTexture() par exemple ou PlaySound() pour la tache sonore par exemple )
    Voila pourquoi j'utilise la virtualité, j'ai tord ?

    Si c'est le kernel qui gère la durée de vie des tâches, pourquoi voudrais-tu les englober dans des xxx_ptr en dehors ? Aucun besoin de scoped_ptr ni de shared_ptr. A moins que ce soient les classes qui les utilisent qui doivent les détruire.
    Le Kernel gère seul la durée de vie des taches . Je ne comprend pas pourquoi les shared_ptr par exemple sont contre-inidiqué pour mon conteneur .
    ( ici il est montré un exemple d'utilisation avec un vecteur : http://www.codeproject.com/vcpp/stl/boostsmartptr.asp )

    tu peux regarder du côté du Design Pattern Visiteur.
    Il faut que je regarde ca ....j'ai déjà oublié ce que c'était

    Si chacune de tes taches est détruite par le kernel lorsqu'elle est retirée de celui-ci, cela ne devrait pas poser un énorme problème.
    Une tache retirée du Kernel est détruite en même temps.

    Encore une fois, non, ce n'est pas parce que tu as plusieurs pointeurs sur une même donnée qu'il te faut obligatoirement utiliser les shared_ptr. De ce que tu dis, je comprends que tu souhaites que le Kernel ait l'ownership de tes Tasks. Donc tu peux avoir autant de pointers que tu veux sur tes tâches, la seule chose à faire est de s'assurer qu'aucun delete n'est appelé sur ceux-ci en dehors de ta classe Kernel.
    Au temps pour moi ! En effet je peux simplement avoir des pointeur dans ma classe qui en a besoin et pas forcément des shared_ptr . Il faut juste que je m'assure qu'aucun de ses pointeur n'est utilisé lorsque la task a été retirée du kernel .

    Je pense que mon plus gros problème reste conceptuel , je vais travailler ca sur papier un peu ... ca a toujours été mon plus gros problème, l'échange de donnée entre module :/
    Merci de vos commentaires .

  7. #7
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    Enfin chacune de ces classes dérivées devra réaliser des taches bien différentes et pour cela posséder des méthodes particulières ( LoadTexture() par exemple ou PlaySound() pour la tache sonore par exemple )
    Et ces méthodes particulières, elles sont appelées par qui et à quel moment ? Ca ne pourrait pas être dans un genre de Execute() virtuel par exemple ?

    Le Kernel gère seul la durée de vie des taches . Je ne comprend pas pourquoi les shared_ptr par exemple sont contre-inidiqué pour mon conteneur
    On parlait de ce que tu utilises en dehors ; mais pour le conteneur en lui-même c'est un bon choix
    Tu peux aussi utiliser directement un map_ptr (ou un truc dans le genre).

    Il faut juste que je m'assure qu'aucun de ses pointeur n'est utilisé lorsque la task a été retirée du kernel .
    Si tu stockes des shared_ptr au niveau de ton kernel, tu peux renvoyer des weak_ptr, il me semble qu'ils sont avertis lorsque la donnée pointée est détruite (c'est donc testable). Enfin c'est à vérifier.

  8. #8
    Provisoirement toléré
    Profil pro
    Inscrit en
    Février 2008
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 439
    Points : 495
    Points
    495
    Par défaut weak_ptr
    Citation Envoyé par Laurent Gomila Voir le message
    Si tu stockes des shared_ptr au niveau de ton kernel, tu peux renvoyer des weak_ptr, il me semble qu'ils sont avertis lorsque la donnée pointée est détruite (c'est donc testable). Enfin c'est à vérifier.
    Oui, on peut demander à un boost::weak_ptr s'il est encore connecté à un boost::shared_ptr[1], et si donc on peut récupérer une copie de ce boost::shared_ptr à partir du boost::weak_ptr. Après, il faut savoir si tu peux gérer le cas où le weak_ptr a expiré au niveau du client.

    [1] Enfin, presque, parce que l'interface est hyper mal conçue : il y a une race inévitable en multitache, et il faut se préparer à une exception alors qu'on a vérifié boost::weak_ptr::expired() avant : en clair, il faut gérer la même condition deux fois! C'est une absurdité genre access(), les exceptions en plus. Je crois qu'on ne peut pas faire plus débile. Mais le comité C++, pour une fois, n'a pas directement entériné cette "spécification" pourrie sans la lire (comme quand il avait recopié et normalisé les détails d'implémentation de la STL).

  9. #9
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Il est clair que boost::weak_ptr::expired est très peu utile... Voire totalement inutile. La façon classique de tester si tout va bien, c'est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    boost::shared_ptr<T> data = myWeakPtr.lock();
    if (data)
    {
      // Tout va bien, ou peut utiliser data
    }
    else
    {
      // La donnée pointée a disparu
    }
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

Discussions similaires

  1. [Débutant] Petite question sur l'héritage
    Par takinelinfo dans le forum C#
    Réponses: 7
    Dernier message: 22/06/2011, 16h16
  2. Petite question sur l'héritage
    Par Ydalb dans le forum Débuter avec Java
    Réponses: 4
    Dernier message: 15/07/2009, 17h08
  3. Petite question sur les héritages?
    Par razonback dans le forum C++
    Réponses: 4
    Dernier message: 15/02/2009, 15h13
  4. petite question sur l'héritage et les cast
    Par baedal dans le forum Langage
    Réponses: 3
    Dernier message: 29/02/2008, 00h48
  5. Petite question sur les performances de Postgres ...
    Par cb44 dans le forum PostgreSQL
    Réponses: 5
    Dernier message: 13/01/2004, 13h49

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