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 :

Héritage en diamant et méthodes virtuelles


Sujet :

C++

  1. #1
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 136
    Points
    23 136
    Par défaut Héritage en diamant et méthodes virtuelles
    Bonjour,

    J'ai deux classes virtuelles pures :
    - Accessor qui défini un accesseur pour des données qui défini void m_open(void) = 0;
    - AccessorRW qui hérite de Accessor (virtual public) et qui défini un accesseur pour des données qui bloque l'écriture des autres accesseurs sur les données et qui doit utiliser m_open(void)

    J'ai 2 classes qui héritent de Accessor (virtual public) et qui définissent void m_open(void).
    - AccessorR : accèdent aux données en lecture seule
    - AccessorW : accèdent aux données en écriture

    Et je veux créer deux classes :
    - AccessorRWR : hérite de AccessorRW et de AccessorR ;
    - AccessorRWW : hérite de AccessorRW et de AccessorW ;

    Toutes ces classes sont templates.


    Or lorsque je veux utiliser des membres des classes parentes, je suis obligé de spécifier la classe :
    .Accessor<T>::attribut; alors qu'avec l'héritage publique je devrait théoriquement pouvoir directement faire : attribut; (sinon le compilateur m'insulte car le membre ne serait pas déclaré) serait-ce dû au fait que toutes mes classes sont template ?
    Ceci devient problématique lors de l'utilisation de m_open dans AccessorRW.
    Déjà suis-je obligé de redéfinir m_open dans AccessorRWR s'il est déjà défini dans AccessorR ?
    Dans le doute je l'ai quand même fait.

    Mais lorsque je compile le compilateur m'insulte encore :
    - si je met Accessor<T>::m_open() il dit qu'il ne trouve pas la référence de Accessor<T>::m_open().
    - si je met m_open(), il me dit :
    error: there are no arguments to ‘m_open’ that depend on a template parameter, so a declaration of ‘m_open’ must be available
    .

    Voici le code incriminé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template<class Type> //Rq : P est un typedef pour la mère : Accessor
        void AccessorRW<Type>::open(void)
        {
            P::m_parent.m_mutex.lock();
            m_open();
            P::isOpen = true;
        }
    Je récapitules donc mes questions :
    - est-il normal que je soit obligé de spécifier la classe mère contenant les attributs ( visibilité protected ) alors que j'hérite de ceux-ci ?
    - dans le cas d'un héritage par diamant, si j'implémente dans une branche une méthode virtuelle pure, est-ce que je peux appeler cette méthode dans la seconde branche ?
    - comment appeler m_open dans AccessorRW ?

    EDIT : merci à gbdivers et à LittleWhite pour leur aide sur le chat.
    Pour que ça fonctionne, il faut mettre :

  2. #2
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Bonjour,

    Il me manque un élément pour en être certain, mais je pense que ceci pourrait t'aider :
    http://www.comeaucomputing.com/techt.../#whythisarrow

    Il faut préciser quand on parle d'héritage et de template comment le lien est fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    template<class T>
    struct A {};
     
    template<class T>
    struct B : A<T> {};
     
    //Ou
     
    template<class T>
    struct B : A<int> {};
    N'ont pas les même implication au niveau de l'accessibilité (c'est la notion de "dependent base class").

    En utilisant ta solution, alors ça "fonctionne", cependant la solution du this-> en probablement une meilleur solution et ne devrait pas te conduire aux même erreurs (A<T>:: force la sélectionne de la fonction, mais ne conduit pas à une résolution dynamique de l'appel, d'où l'erreur).

    Pour ta question sur m_open, normalement non. L'idée est qu'il ne faut pas qu'il y ai d’ambiguïté sur le "final overrider".

  3. #3
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 617
    Points
    15 617
    Par défaut
    (je me souvenais de ce problème, mais sans avoir de source pour l'explication, je lui ai dit que, te connaissant, tu allais nous trouver ça sans problème )

  4. #4
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 136
    Points
    23 136
    Par défaut
    Bonjour,

    Merci pour ces précisions, je n'ai pas tout compris mais je vais essayer de relire plusieurs fois et plus lentement^^

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    Généralement, quand on en vient à parler d'héritage en diamant, la première réaction (et sans doute la plus sensée) à avoir, c'est de se dire "attention, danger, problème de conception potentiel"

    Dans le cas présent, je ne suis pas loin de penser que c'est effectivement le cas

    Mais, pour une fois changer, ce n'est pas LSP qui nous permet de nous en rendre compte.

    Non, ce qui nous permet de nous rendre compte qu'il y a un problème de conception, ce sont les règles de programmation par contrat, et, plus précisément le fait que les post conditions ne puissent pas être affaiblies dans la sous classe...

    C'est un peu le même principe (mais à l'envers) que celui qui fait qu'une liste triée ne peut pas hériter d'une liste non triée

    En gros, ici, si AccessorRWR est une classe qui permet l'accès en lecture / écriture et qu'elle hérite de AccessorR qui ne permet l'accès qu'en lecture seule, tu affaiblit les post conditions dans le sens où tu as une post condition dans AccessorR qui dira (même si tu ne le testes pas) que les données doivent être identiques après la lecture à ce qu'elles étaient avant la lecture et que cette post condition sera levée à partir du moment où AccessorRWR accepterait que les données soient modifiées après l'accès

    A partir de là, tu n'as plus que deux solutions :

    Soit, AccessorRWR hérite de AccessorRW et rajoute les fonctions manquant à l'interface pour fournir les possibilités de la lecture seule, soit AccessorRWR hérie directement de Accessor et implémente l'ensemble des services dont tu as besoin.

    En fait, il reste une troisième solution, étant donné que tu pars, de toutes manières, sur le principe d'une classe template: la création de politiques...

    L'idée serait de créer trois politiques (éventuellement template) totalement distinctes:
    • une politique "read only" qui ne fournit que les fonctions de lecture seule
    • une politique "write only" qui ne fournit que les fonctions d'écriture seule
    • une politique "read write" qui fournit aussi bien les fonctions de lecture que les fonctions d'écriture
    tu pourrais alors partir sur une classe Accessor proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename Type, type OpenModePolicy, typename AccessPolicy>
    class Accessor;
    que tu spécialiserais partiellement en fonction des politiques, voir des traits...

  6. #6
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 136
    Points
    23 136
    Par défaut
    Bonjour,

    Je pourrais en effet faire un design pattern politique mais je serais toujours obligé d'avoir :
    - SafeThreadAccessor; //nécessaire du fait de quelques méthodes qui doivent prendre un & SafeThreadAccessor<Type>
    - SafeThreadAccessorRorW<Mode_access>; // remplace SafeThreadAccessorReadOnly et SafeThreadAccessorWritteOnly (Writte only est d'ailleurs mal choisit comme nom vu qu'on peut faire de la lecture et de l'écriture)
    - SafeThreadAccessorRW; //idem, nom mal choisit car pas assez explicite. Il permet de passer d'une session à une autre sans être interrompu par une session d'écriture (redéfini 2 méthodes et défini 1 méthode supplémentaires). Nécessaire du fait que certaines méthodes doivent prendre un & SafeThreadAccessorRW<Type>
    - SafeThreadAccessorRW_RorW<Mode_access>;

    Donc je ne pense pas qu'un design politique puisse m'apporter grand chose. Je pense que la priorité serait de renommer mes classes de façon plus explicite

    Je pense mettre mon code sur github assez vite, veux-tu que je mette un lien ici?
    Par contre les commentaires sont écris en anglais façon Neckara

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    A vrai dire, peut etre ai-je mal compris, mais, il me semble qu'il y a deux "concepts" relativement orthogonaux dans ton design:

    D'un coté tu as la gestion mono / multi threadé, et de l'autre le concept d'acces Read / Write /RW, avec comme résultat le fait que tu as six combinaison possibles.

    Si tel est bel et bien ton souhait, je persiste et signe en te conseillant avec la plus grande insistance à prévoir une gestion générique de ton système dans laquelle l'un des argument template serait la politique de "gestion de thread" et l'autre celle de gestion des accès, sachant que la gestion de l'accès en RW ne peut (pour les raisons que j'ai invoquées dans ma première intervention) en aucun cas hériter de l'accès read et de l'accès write.

    L'idée n'est pas d'utiliser un quelconque pattern "strategy" ou "policy", mais bel et bien de définir des politiques de traitement, afin de n'avoir qu'un minimum de spécialisations partielles à fournir.

    De cette manière, tu éviterais l'héritage en diamant et tu serais beaucoup plus souple dans ta conception:

    D'un coté, il te devient assez facile de définir un typedef pour une utilisation particulière et, de l'autre, il devient assez aisé de définir de nouvelles politiques, tant pour la gestion des accès concurrents que pour le type d'accès en cas de besoin, et tout fonctionnera "naturellement".

    Si, dans tes politiques de gestion des accès concurrents, tu prévois une fonction lock, une fonction unlock et une fonction "isLocked" (les deux premières ne faisant rien et la troisième renvoyant false si on est dans un contexte mono-threadé), rien ne t'empêche de créer une fonction template dans tes politiques d'accès lecture/écriture qui prend, en paramètre, la politique de gestion des accès concurrents et fait appel aux trois fonctions en question

    Les trois spécialisations partielles que tu devras donner au final à ta classe "principale" étant celles correspondant respectivement aux aspect lecture/ écriture

  8. #8
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 136
    Points
    23 136
    Par défaut
    Aujourd'hui, je vais retoucher mon code pour le rendre plus lisible et explicite et j'en profiterais aussi pour essayer de faire un petit wiki.

    En fait, j'ai 4 types d'Accessor :
    - AccessorReadOnly : lire des données en lecture seule ;
    - AccessorWritteOnly ( nom mal choisit, je pense le renommer) : écrire (et aussi lire) des données ;
    - ainsi que deux AccessorRW (je pense le renommer aussi).

    Les AccessorRW permettent de créer une session d'écriture qui a de grands passages de lectures. Mais on ne peut pas alterner des session de lectures et d'écritures sous peine d'être interrompu par une session d'écriture et de se retrouver avec des résultats faux.
    On va donc utiliser des AccessorRW qui permettent de passer d'une session de lecture AccessorRWR à des sessions d'écritures AccessorRWW sans être interrompu.

    J'ai donc deux concept :
    - lecture/écriture. Mais faire un design pattern politique ne convient pas car il faudrait spécialiser une bonne partie les méthodes deux fois, ça duplique donc le code.
    - passage d'une session à l'autre pouvant être interrompue par un writte/passage d'une session à l'autre ne pouvant pas être interrompue par un writte (faudrait que je trouve un nom plus explicite : AccessorUninterrupted ?); Mais j'ai besoin d'un ancêtre commun pour tous les AccessorUninterrupted. Quitte à faire autant que cet ancêtre définisse tout ce qui fait la déférence des AccessorUninterrupted.
    Au final, il ne me reste plus qu'à faire deux classes héritantes et c'est fini.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Neckara Voir le message
    J'ai donc deux concept :
    - lecture/écriture. Mais faire un design pattern politique ne convient pas car il faudrait spécialiser une bonne partie les méthodes deux fois, ça duplique donc le code.
    Même pas...

    Citation Envoyé par David Wheeler
    all problems in computer science can be solved by another level of indirection
    En se basant sur ce principe simple, tu pourrait créer une class "GenericReader" et une classe "GenericWriter" qui s'occuperait pour l'une de lire les données et pour l'autre de les écrire.

    Pour la version "read only", tu ne ferait appel qu'à "GenricReader", pour la version "write only" (bien qu'elle fusse a priori supprimée), tu ne ferais appel qu'à "GenericWriter" et pour la version "read write", tu ferais appel aux deux.

    Tu ne ferais que pousser un peu plus loin le principe de la délégation des responsabilités

  10. #10
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 136
    Points
    23 136
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Même pas...
    Si car en fonction si on est en read ou en writte, on a pas les mêmes types de retours des méthodes.

    Citation Envoyé par koala01 Voir le message
    En se basant sur ce principe simple, tu pourrait créer une class "GenericReader" et une classe "GenericWriter" qui s'occuperait pour l'une de lire les données et pour l'autre de les écrire.

    Pour la version "read only", tu ne ferait appel qu'à "GenricReader", pour la version "write only" (bien qu'elle fusse a priori supprimée), tu ne ferais appel qu'à "GenericWriter" et pour la version "read write", tu ferais appel aux deux.

    Tu ne ferais que pousser un peu plus loin le principe de la délégation des responsabilités
    Je met le code en ligne dans quelques heures, je te donnerais un lien, je pense que tu comprendras mieux ce que je fait (j'ai même améliorer les commentaires).


    EDIT : Voici les sources : https://github.com/Neckara/IRCServer...inerSafeThread
    Demain faut que je supprime les quelques fichiers qui sont rester malgré leur suppression.

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

Discussions similaires

  1. Héritage, type dynamique et méthode virtuelle
    Par LinuxUser dans le forum C++
    Réponses: 15
    Dernier message: 05/12/2012, 18h30
  2. méthode virtuelle pure et héritage
    Par Invité dans le forum Langage
    Réponses: 3
    Dernier message: 20/07/2009, 22h12
  3. héritage et méthodes virtuelles ?
    Par Didine981 dans le forum C++
    Réponses: 4
    Dernier message: 08/12/2007, 13h43
  4. [Héritage] Downcasting et méthodes virtuelles
    Par poukill dans le forum C++
    Réponses: 8
    Dernier message: 16/07/2007, 13h38
  5. Exceptions, héritage et méthodes virtuelles
    Par Nuwanda dans le forum C++
    Réponses: 13
    Dernier message: 23/05/2006, 12h06

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