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 :

Possibilité d'objet partagé


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 2
    Par défaut Possibilité d'objet partagé
    Bonjour,

    j'aimerais savoir si il est possible de créer des objets que l'on pourrais partager sur plusieurs classe (un peu comme une union en C).

    Pour vous expliquer mon problème est que mon main() contient un élément topology (initialisé et remplit) auquel j'aimerais avoir accès dans presque toutes les fonctions du programme.

    La définition de topology ce fait comme suit:
    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 Topology 
    {
    	public:
    		virtual void init(uint ports, uint vcs, uint credits, uint buffer_size, uint no_nodes, uint grid_size, uint links) = 0;
    		virtual void setup(void) = 0;
    		virtual void connect_interface_processor(void) = 0;
    		virtual void connect_interface_routers(void) = 0;
    		virtual void connect_routers(void) = 0;
    		virtual string print_stats(void) = 0;
    		Topology() {}
    		virtual ~Topology() {}
     
    		unsigned long long int max_sim_time;
    		map< uint , uint > east_links;
    		map< uint , uint > west_links;
    		map< uint , uint > north_links;
    		map< uint , uint > south_links;
    		vector <Router*> routers;
    		vector <Interface*> interfaces;
    		vector <Processor*> processors;
    		vector <GenericLink*> link_a;
    		vector <GenericLink*> link_b;
     
    }
    ;
    Je pourrais le mettre en paramètre de chacune des fonctions mais cela serais trop compliqué et entraînerais sûrement des inclusions cyclique.

    Un petit point à précisé, je débute en POO donc désolé si cette question vous paraît trivial.

    Merci d'avance.

  2. #2
    Invité
    Invité(e)
    Par défaut
    salut,

    regarde du côté du singleton.

  3. #3
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

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

    le plus simple c'est un simple singleton.
    Tu vires le constructeur qui passe protected/private et tu récupères l'instance via une méthode statique
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Topology {
     private: Topology() {}
    public: static Topology& Get() {
      static Topology sing;
      return sing;
      }
    };
     
    Topology& myTopology = Topology::Get();
    Sinon de manière générale, il faut juste un pointeur/référence à partager entre ceux qui ont besoin de connaître l'objet/instance en question.
    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.

  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,

    Si, déjà, tu en es à devoir utiliser ta classe "partout", j'aurais vraiment tendance à dire qu'il y a, dés le départ, un problème de conception.

    Sans rien savoir de ton projet, je ne suis pas loin de penser qu'il y a au moins l'un des principes repris ci-dessous (et, qui sait, peut etre même plusieurs ) qu'il serait bon de prendre en compte:
    1. SRP ( Single Responsability Principle ) : le principe de la responsabilité unique >> si une classe ou une fonction prend plus d'une responsabilité, c'est sans doute qu'elle en a trop
    2. Loi Demeter >> si une classe A utilise la classe B et que la classe B utilise elle-même la classe C, A ne devrait pas avoir besoin de connaitre la classe C pour pouvoir utiliser la classe B
    3. DIP ( Dependnecies Inversion Principle ) : Principe de l'Inversion des Dépendances >> les parties "de haut niveau" ne devraient pas dépendre des détails d'implémentations
    4. ISP ( Interface Segregation Principle ) : Principe de la Ségrégation des Interfaces >> les classes abstraites ne devraient pas dépendre de classes concrètes : ce sont les classes concrètes qui devraient dépendre des classes abstraites
    Ce n'est sans doute pas la cause de ton malheur (c'en serait plutot une conséquence ), mais je ne serais pas surpris que, si tu continue dans cette voie, tu finisses tot ou tard par éprouver de très grandes difficultés à respecter un dernier principe: OCP (Open / Close Principle) : Le principe ouvert / fermé >> ton code doit rester ouvert aux évolutions, mais etre fermé aux modifications (tu ne devrais pas avoir à modifier l'existant pour ajouter une fonctionnalité)

    Vraiment, je ne saurais trop t'inciter à essayer de réfléchir à la manière d'éviter d'avoir à te trimballer une instance unique de Topology, d'autant plus que tu sembles décidé à permettre qu'elle serve de base à un héritage public comme en atteste la présence de fonction virtuelles pure...

    La notion de singleton se marie en effet relativement mal avec celle d'héritage public quand la classe de base n'est pas une classe template
    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
    Invité
    Invité(e)
    Par défaut
    Vraiment, je ne saurais trop t'inciter à essayer de réfléchir à la manière d'éviter d'avoir à te trimballer une instance unique de Topology, d'autant plus que tu sembles décidé à permettre qu'elle serve de base à un héritage public comme en atteste la présence de fonction virtuelles pure...
    ben en supposant que Topology ca soit une entité, qui n'a aucun sens d'être copiée. Tu vas pas la copier dans toutes les classes.
    Par ailleurs la gueule que ca a avec un nom pareil, c'est plus une datastructure, donc vaut mieux éviter les dupplications.
    Je suis pas proneur du Singleton, mais pour le coup, je vois pas trop d'alternatives.

  6. #6
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Par défaut
    Bonjour.

    La classe Topology est une classe virtuelle pure... Quelque chose m'échappe.

    Citation Envoyé par Xilyos Voir le message
    mon main() contient un élément topology (initialisé et remplit)
    C'est quoi un élément topology (un objet qui dérive de topology ?). Merci de préciser.

  7. #7
    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
    Citation Envoyé par galerien69 Voir le message
    ben en supposant que Topology ca soit une entité, qui n'a aucun sens d'être copiée. Tu vas pas la copier dans toutes les classes.
    Par ailleurs la gueule que ca a avec un nom pareil, c'est plus une datastructure, donc vaut mieux éviter les dupplications.
    Je suis pas proneur du Singleton, mais pour le coup, je vois pas trop d'alternatives.
    Si ce n'est que...

    Si tu regardes bien, tu te rendras compte que Topology est remplie de fonctions virtuelles pures, ce qui fait que l'on ne peut pas instancier un objet de type Topology.

    Il faut donc faire hériter une autre classe qui définira les fonctions virtuelles pures.

    En soit, ce n'est pas très grave (pour autant que l'on travailles alors avec un pointeur sur Topology, quitte à renvoyer "ce qui est pointé" sous forme de référence), mais, le but du singleton est... de ne permettre à une seule instance de la classe d'exister.

    Quel que soit le nombre de classes que l'on fera dériver de Topology (et le simple fait que l'on ouvre la porte à l'héritage, implique que n'importe qui peut décider à sa guise de faire hériter une de ses classes de Topology), on ne pourra donc en tout état de cause jamais avoir qu'un et un seul objet de type (dérivant de) Topology à un instant T.

    Ce n'est pas forcément faux, mais tu avoueras que cela place quand même une très sérieuse restriction à l'utilisation des classes dérivées

    En outre, au vu des noms des fonctions, je subodore réellement qu'il y a au moins trois responsabilités distinctes à cette interface, ce qui en fait, au minimum deux de trop

    Personnellement, je ne suis pas loin de me dire que je préférerais sans doute cacher purement et simplement l'existence de l'instance de Topology (en déclarant une variable globale directement dans le *.cpp qui contient l'implémentation de quelques fonctions qui l'utilisent) et en créant des fonctions libres qui invoqueraient directement la fonction adéquate au départ de cette variable "globale".

    Cela pourait donner quelque chose comme
    TopoFunc.h;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void connect_interface_processor();
    void connect_interface_routers();
    void connect_routers();
    string print_stats();
    TopoFunc.cpp
    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
    /* oui!, je sais, cela peut paraitre bizare, mais, comme on n'utilisera Topology qu'ici,
     * autant éviter à l'utilisateur de savoir que ca existe :D
     */
    class Topology 
    {
        public: 
            /*... */   
    } topo;
    void connect_interface_processor()
    {
        topo.connect_interface_processor();
    }
    void connect_interface_routers()
    {
        topo.connect_interface_routers();
    }
    void connect_routers()
    {
        /* dans la meme veine */
    }
    string print_stats(void)
    {
        /* dans la meme veine */
    }
    Au moins, de cette manière, l'instance de Topography sera un minimum protégée, ne pouvant être utilisée que... par les fonctions qui sont définies dans le fichier *.cpp.

    Ce ne sera toujours pas l'idéal, mais cela permettra, au moins, d'éviter quelques cas susceptibles de créer des effets de bord indésirables

    Pour terminer sur quelques détails troublants:
    1. La fonction init devrait bien plus surement être remplacée par le constructeur
    2. La fonction setup peut éventuellement rester, si tant est qu'elle permette de remettre l'objet "en état" après un problème
    3. Les fonction connect_XXX risquent fortement d'avoir besoin de paramètres
    4. Il est généralement utile de travailler "en mirroir" : si une fonction permet de faire quelque chose, il est souvent de bon ton de fournir une fonction qui permet de le défaire (surtout si la fonction s'appelle "connect" )
    5. Il est rarement bon qu'une classe d'interface (car c'est bel et bien de cela qu'il s'agit, étant donné que toutes les fonctions sont virtuelles pures) contienne des données, surtout si se sont des collections de pointeurs sur des classes qui semblent très clairement destinées à être dérivées... Il est généralement préférable de laisser ce soin aux classes qui implémentent l'interface

    Citation Envoyé par moldavi Voir le message
    Bonjour.

    La classe Topology est une classe virtuelle pure...
    En fait, mais bon, ce n'est qu'un détail, on parle de classe abstraite, c'est à dire une classe ne pouvant pas être instanciée en tant que telle du fait de la présence d'au moins une fonction virtuelle pure (il n'y a que les fonctions qui puissent être virtuelles et que les fonctions virtuelles qui puissent être pures )
    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

  8. #8
    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 : 50
    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
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    ben en supposant que Topology ca soit une entité, qui n'a aucun sens d'être copiée. Tu vas pas la copier dans toutes les classes.
    Par ailleurs la gueule que ca a avec un nom pareil, c'est plus une datastructure, donc vaut mieux éviter les dupplications.
    Je suis pas proneur du Singleton, mais pour le coup, je vois pas trop d'alternatives.
    Que Topology soit une classe de type entité et non pas valeur, pourquoi pas. Je n'ai pas compris son rôle, donc je ne serai pas définitif sur le sujet, mais ça ne me semble pas délirant, et en tout cas la manière dont elle est mise en place va bien dans ce sens.

    Qu'une classe de type entité ne supporte pas la copie, c'est très bien.

    De là à dire qu'elle devrait être singleton, il y a un énorme pas à franchir. Un singleton n'est pas seulement une classe ne supportant pas la copie, mais une classe dont il ne peut exister qu'une seule instance, ce qui est tout de même très différent (un compte bancaire est non copiable, mais une banque a intérêt à gérer plusieurs comptes pour être rentable).

    Un effet annexe de l’existence d'une seule instance est que du coup il est possible de mettre en place un mécanisme pour accéder à cette instance unique depuis n'importe où dans le programme. Et c'est cet accès depuis n'importe où sans passer l'instance en argument, plus que l'instance unique, qui a fait le "succès" du singleton, mais qui est aussi son principal problème.

    Déjà, on peut noter qu'il existe d'autres mécanismes que le singleton si on veut juste donner accès à une instance depuis n'importe où. Par exemple la variable globale (c'est ce qu'on utilise avec std::cout). Ou encore une fonction retournant une variable statique (exemple : std::locale::classic).

    L'avantage de ces alternatives est qu'elles permettent quand même à d'autres instances de la classe d'exister. Dans le cas qui nous intéresse, ça ne me semble pas absurde a priori de vouloir dans un même programme faire des calculs avec deux topologies différentes, et en comparer le résultat.

    L'inconvénient de ces alternatives, c'est qu'à la base, la notion même d'avoir un objet accessible depuis n'importe où sans être passé en argument, bien que pratique et séduisante au premier abord, pose problème. Elle pose problème parce que du coup, on a un état caché dans toutes les fonctions du programme. Qu'on ne peut plus faire aucun test unitaire d'aucune fonction en regardant uniquement ses arguments et ses valeurs de retour, mais qu'il faut tenir compte de cet état caché. Que l'on ne peut plus raisonner sur le code sans devoir se dire que potentiellement le résultat de toute fonction peut dépendre de cet état caché. Et pour peu que cet objet accessible de partout soit modifiable, on ne peut plus exécuter aucun code en parallèle à cause de cet état caché, on sait plus si on peut écrire du code réentrant, des callbacks... à cause de cet état caché. Cette notion d'état caché utilisé en interne d'une fonction sans faire partie de l'interface est par exemple au cœur de fonctions comme strtok. Et c'est la raison pour laquelle strtok est complexe, génératrice d'erreur et globalement considérée comme à éviter.

    Alors que faire ? Le plus simple est probablement de passer la topologie en paramètre de chaque fonction devant l'utiliser (quand je dis passer en paramètre, bien entendu, pas par copie. Cette classe reste non copiable, et est pssée par référence, de préférence constante). Du coup, on sait quelle fonction l'utilise, quelle fonction ne l'utilise pas. On peut faire simultanément des calculs avec des topologies différentes. On sait que si une fonction n'a pas cet argument, elle n'utilise pas la topologie, et que donc on n'a pas besoin de la tester dans différents cas de topologie, voir si elle marche encore.

    Une alternative qui peut être pratique dans certains cas, si les fonctions dépendent d'autres éléments de contexte et que tous les passer alourdisse vraiment l'écriture, c'est de rassembler ces éléments dans une seule structure, et de passer uniquement cette structure aux fonctions en ayant besoin, que ce soit en tant qu'argument classique, ou que ce soit en faisant de ces fonction non plus des fonctions libres, mais des fonctions membres d'une classe dont le rôle serait de maintenir ce contexte de calcul.

    Remplacer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void f(Topology const &t, double x1, double x2)
    {
      // ...
      w = t.max_sim_time;
      g(t, z);
      // ...
    }
     
    int main()
    {
      Topology t(...);
      cout << f(t, x1, x2);
    }
    Non pas par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void f(double x1, double x2)
    {
      // ...
      w = globalTopology().max_sim_time;
      g(z);
      // ...
    }
     
    int main()
    {
      Topology t(...);
      setGlobalTopology(t);
      cout << f(x1, x2);
    }
    Mais par :
    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
    class Simulation
    {
      void f(double x1, double x2);
      void g(double d);
      Topology &myTopo;
    };
     
    void Simulation::f(double x1, double x2)
    {
      // ...
      w = myTopo.max_sim_time;
      g(z);
      // ...
    }
    int main()
    {
      Topology t(...);
      Simulation s(t /*plus autres params*/);
      cout << s.f(x1, x2);
    }
    Je ne dis pas que cette méthode est supérieure à la première dans l'absolu, il y a juste des cas où elle est plus pratique, en particulier si la classe Simulation doit aussi conserver d'autres états que la topologie. Et des cas où passer l'argument est plus simple.

    Mais j'ai du mal à voir des cas où le singleton est supérieur.
    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.

  9. #9
    Invité
    Invité(e)
    Par défaut
    dans ton deuxième exemple, tu es en train d'écrire un wrapper uniquement pour stocker Topology...
    Tu crèes en plus une dépendance sur Topology car dans l'interface de ton wrapper (contrairement à accédé via singleton depuis cpp)

    L'argument des tests unitaires est discutable. Il te suffit de supprimer le singleton à la fin de chaque test. On peut discuter si on tourne en parallèle, notamment, si on sein d'une méthode utilisant la variable globale celle-ci est altérée entre temps et corrompt de fait le traitement de la méthode.

    Toujours est-il que pallier ce problème équivaut à copier l'entité et introduire une désynchronisation, ce qui est accessoirement dangereux

    Je suis d'accord que le singleton n'est pas la meilleure solution, ya toujours mieux, mais pour le coup, éviter de passer en paramètre partout me via cette méthode me semble ok.
    edit: mon petit doigt me souffle que c'est pour du driver pour un projet pas très gros, et que le projet est orienté plus transaction que services (spaghetti like)

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

Discussions similaires

  1. Objets partagés par les noeuds d'un cluster JBoss
    Par jbossdev dans le forum Wildfly/JBoss
    Réponses: 1
    Dernier message: 08/09/2006, 13h48
  2. [Débutant] Utilisation d'un objet "partagé"
    Par Floyd_C dans le forum C++
    Réponses: 6
    Dernier message: 28/07/2006, 10h26
  3. Grouper des objets partageant des propriétés
    Par camboui dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 06/04/2006, 19h01
  4. [Kylix] Objet Partagé et la CLX
    Par Sogarf dans le forum EDI
    Réponses: 3
    Dernier message: 25/05/2005, 11h21
  5. Peux t'on créer une copie locale de l'objet partagé?
    Par Anonymous dans le forum CORBA
    Réponses: 8
    Dernier message: 16/04/2002, 16h20

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