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

Langage C++ Discussion :

Héritage et abstraction


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut Héritage et abstraction
    Bonjour,
    J'ai besoin de votre avis éclairé.

    Pour être plus précis, j'aimerais savoir ce que vous pensez des déclarations suivantes.
    Je ne donne pas d'explications volontairement pour le moment, même si je laisse quelques indices.
    Une fois que quelques réponses auront été donnés, je détaillerai un peu plus.

    À titre informatif, je ne suis pas un débutant, et je suis loin d'être un expert.

    Merci d'avance !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class BaseAbstraite
    {
     
      protected:
        BaseAbstraite();
        BaseAbstraite(BaseAbstraite const&);
        BaseAbstraite& operator = (BaseAbstraite const&);
     
      public:
        virtual
        ~BaseAbstraite() = 0;
     
    }; // class BaseAbstraite

  2. #2
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juillet 2012
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juillet 2012
    Messages : 8
    Par défaut
    Je ne sais pas trop ce que tu attends mais je vais essayer.

    Ta déclaration est valable mais il faut implémenter le destructeur même s'il est virtuel pur.

    Ce type de déclaration de classe montre pour moi certaines limites du C++ et entre autre l'impossibilité de déclarer une classe abstraite avec un mot clé comme Abstract (cf. Java),
    ce qui amène à déclarer un destructeur virtuel pur alors que ça n'a aucun sens.

  3. #3
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 770
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 770
    Par défaut
    Citation Envoyé par SaBe94 Voir le message
    ce qui amène à déclarer un destructeur virtuel pur alors que ça n'a aucun sens.
    Si cela a du sens :
    • virtuel pure veut dire "je ne donne pas l'implémentation à cette méthode, ce sont les classes filles qui doivent le faire".
    • virtuel veut dire "je donne une implémentation par défaut à cette méthode, libre aux classes filles de la redéfinir".


    Édit: @Iradrille: Effectivement, il y a virtuel et virtuel pur


    Et excuse-moi du peu par rapport au Java :
    1. J'ai toujours perdu mon latin entre les herits extends interface class, sans parler des static final et package
    2. Java 1.8 permet de coder des interfaces avec une implémentation par défaut. Comme quoi



    Citation Envoyé par SaBe94 Voir le message
    entre autre l'impossibilité de déclarer une classe abstraite avec un mot clé comme Abstract (cf. Java),
    Certes, mais le compilateur te rappelle à l'ordre si tu essayes d'utiliser une classe abstraite autrement que pour de l'héritage

  4. #4
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Citation Envoyé par foetus Voir le message
    Si cela a du sens : virtuel veut dire "je ne donne pas l'implémentation à cette méthode, ce sont les classes filles qui doivent le faire".
    Sauf qu'un destructeur pur n'a pas de sens et devra quand même être défini. Bah oui, la classe fille appel toujours le destructeur de la classe mère. C'est ballot de devoir définir une fonction pure.

    Citation Envoyé par foetus Voir le message
    J'ai toujours perdu mon latin entre les herits extends interface class, sans parler des static final et package.
    C'est quand même un peu pareil avec les fonctions membres qui peuvent avoir static, inline, constexpr, const, volatile, final, une ref-qualifiée, override, virtuelle, noexcept, noreturn (ou autres attributs), delete, probablement bientôt requires (concept), le type de retour à droite, à gauche, decltype et auto. Heureusement qu'on peut séparer par des virgules les prototypes avec un même type de retour... .

  5. #5
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par foetus Voir le message
    Si cela a du sens : virtuel veut dire "je ne donne pas l'implémentation à cette méthode, ce sont les classes filles qui doivent le faire".
    Virtuel veut dire "je donne une implémentation par défaut, les classes dérivées PEUVENT changer l'implémentation".
    Virtuel pur veut dire "je suis autorisé à ne pas donner d'implémentation par défaut (et c'est le cas général), les classes dérivées DOIVENT implémenter la fonction; même si je donne une implémentation par défaut la classe est abstraite et donc non instanciable".

    Si la classe n'a pas d'autres fonctions virtuelles pures, c'est le seul moyen de la rendre abstraite.

    Un constructeur protégé empêche l'instanciation "depuis l’extérieur", mais pas "depuis l'intérieur".
    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
    struct Foo {
       static Foo create() {
          return Foo(); // instanciation possible depuis l'intérieur de la classe
       }
     
    protected:
       Foo() { }
    };
     
    Foo f1 = Foo::create(); // ok
    Foo f2; // erreur - constructeur protégé
     
    struct Bar {
       virtual ~Bar() = 0 { }
     
       static Bar create() {
          return Bar(); // erreur - Bar abstraite
       }
    };
     
    Bar b; // erreur - Bar abstraite

  6. #6
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    a- Constructeur de copie et opérateur d'affectation devraient être purement et simplement supprimés -> "= delete", ou hérite de boost::noncopyable (qui reste ma façon de faire préférée).
    b- Le destructeur virtuel pur doit quand même être défini comme cela a été signalé
    c- Qu'il y ait un constructeur par défaut ou non, qu'il soit public ou protégé ne changera rien : la classe est abstraite et seules les filles pourront faire appel au constructeur.
    d- Le nom est tout pourri : il ne transporte aucune sémantique ; mais je soupçonne que pour l'instant c'est fait pour
    e- "protected" est devenu un red flag chez moi du coup je cherche systématiquement s'il y a des données avec cette visibilité. L'objectif de l'encapsulation, c'est de garantir l'invariant d'une classe. Or quand il y a des données protégées, cela veut dire que les classes filles devront faire attention à continuer à garantir les invariants de la classe mère lorsqu'elles iront toucher aux données protégées. Même chose avec les fonctions protégées. J'ai aujourd'hui envie de dire qu'une fonction ne devrait pas être autorisée à être protégée si elle ne peut pas garantir les invariants tout comme les fonctions publiques. [Mais je m'égare, "protected" a été employé pour tout autre chose, certes inutile -> c-]

    EDIT:
    PS: si vous cherchez à savoir comment rendre une classe non-instanciable, la seule façon c'est de la rendre abstraite, et seule une fonction membre virtuelle (aka méthode -- qui englobe officiellement les 3 mots en C++) pure permet cela. Il y a une définition mal formulée dans la norme à ce sujet.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  7. #7
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Bien, merci pour vos réponses.
    Le but était de voir ce qui saute aux yeux avant d'orienter la discussion, du coup j'ai obtenu à peu près les réponses auxquelles je m'attendais…
    Mais aussi des choses plus intéressantes.

    Le code présenté est censé représenter une classe dont la seule finalité est d'être au sommet d'une hiérarchie.
    Par exemple, une classe XMLElement qui permet de manipuler n'importe quel élément XML, peu importe que ce soit une balise, du texte, un commentaire, etc., mais qui, en soit (j'entends, seule), ne sert à rien.

    Une telle classe n'a pas vocation à être instanciée.
    Pour cela, il y a a priori deux moyens :
    1. ne laisser aucun constructeur public ;
    2. rendre la classe abstraite (via une fonction membre virtuelle pure).
    Sauf que comme l'a très bien fait remarquer @Iradrille, la première méthode permet l'instanciation « depuis l'intérieur », à savoir depuis la classe elle-même, les classes filles (constructeurs protégés), ou même les amis.
    Pour autant, si le programmeur n'écrit pas de bêtises, cela n'a pas vraiment d'importance. (Si ?)

    [EDIT]Après réflexion, le programmeur original ne maîtrise pas les classes filles écrites par d'éventuels utilisateurs, qui pourraient tout à fait accéder aux constructeurs protégés.
    Oubliez ma dernière réflexion[/EDIT]

    Ceci dit, est-ce une erreur, une faute, de combiner les deux ?
    Certes, dès lors qu'une classe est abstraite, aucun constructeur ne peut être appelé directement, peu importe sa visibilité.
    Il est donc inutile de chercher à les masquer ; mais pour autant, ce n'est pas interdit.

    Donc en gros, c'est cela ma question.
    Est-ce que c'est :
    • « inutile, mais à la rigueur pourquoi pas » ;
    • une très bonne idée ;
    • une faute de programmation ;
    • une erreur de conception ;
    • quelque chose de pas si mauvais, mais à éviter tout de même ;
    • etc.


    À la réflexion, j'ai le sentiment que le vrai problème est posé dans la discussion proposée par @Luc Hermitte.
    (Après tout, avant de se rendre compte que la réponse était 42, il a d'abord fallu trouver la (vraie) question…)
    La discussion est très intéressante, et complètement dans le sujet selon moi.
    Je ne comprends pas trop pourquoi le lien a été retiré du post…

  8. #8
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur R&D
    Inscrit en
    Juillet 2012
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juillet 2012
    Messages : 8
    Par défaut
    Citation Envoyé par foetus Voir le message
    Si cela a du sens :
    • virtuel pure veut dire "je ne donne pas l'implémentation à cette méthode, ce sont les classes filles qui doivent le faire".
    • virtuel veut dire "je donne une implémentation par défaut à cette méthode, libre aux classes filles de la redéfinir".
    Je dis que ça n'a pas de sens parce que la règle générale veut que quand je déclare une méthode virtuelle pur, je ne dois pas donner d'implémentation à cette méthode. Les classes filles doivent les redéfinir.

    Mais essaie le code suivant :

    Code C++ : 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 CAbstract
    {
    public:
    	CAbstract() {};
     
    public:
    	virtual ~CAbstract() = 0;
    };
     
    class CDerived : public CAbstract
    {
    public:
    	CDerived() {};
    public:
    	virtual ~CDerived() {};
    };
     
    int main()
    {
    	{
    		CDerived d;
    	}
    return 0;
    }

    Erreur de compilation. Le compilateur devrait se plaindre de ~CAbstract.

    Pour corriger ce problème il faut donc implémenter le destructeur et ce bien qu'il soit déclaré virtuel pur.

    Je change donc mon code.

    Code C++ : 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 CAbstract
    {
    public:
    	CAbstract() {};
     
    public:
    	virtual ~CAbstract() = 0 { cout << "CAbstract destructor" << endl; };
    };
     
    class CDerived : public CAbstract
    {
    public:
    	CDerived() {};
    public:
    	virtual ~CDerived() { cout << "CDerived destructor" << endl; };
    };
     
    int main()
    {
    	{
    		CDerived d;
    	}
    return 0;
    }

    Là ça compile et je devrais voir comme trace sur ma console : "CDerived destructor" et "CAbstract destructor".

    Pour moi, en faisant cela on casse deux principes :
    1. Ne jamais implémenter une méthode virtuelle pure

    1. Ne jamais appeler une méthode virtuelle pure


    Au final, on déclare le destructeur virtuel pur uniquement quand on souhaite que notre classe soit non instanciable et qu'on ne voit pas quelle autre méthode peut être déclarée virtuelle pure.

    Ce n'est donc qu'une astuce parce qu'il n'existe pas un mot clé pour déclarer la classe non instanciable.

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

Discussions similaires

  1. [PHP 5.3] Abstract méthode en héritage
    Par Général03 dans le forum Langage
    Réponses: 3
    Dernier message: 21/10/2011, 11h31
  2. Abstract et héritage
    Par nicdo77 dans le forum Langage
    Réponses: 4
    Dernier message: 18/11/2009, 14h05
  3. [Postgres] Héritage + Clés
    Par k-reen dans le forum PostgreSQL
    Réponses: 6
    Dernier message: 21/05/2003, 16h37
  4. Abstract VS virtual
    Par LE CHAKAL dans le forum Langage
    Réponses: 2
    Dernier message: 29/08/2002, 17h50
  5. Héritage entre Forms
    Par BarBal dans le forum Composants VCL
    Réponses: 7
    Dernier message: 29/08/2002, 17h44

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