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 :

utiliser une fonction d une class dans une autre


Sujet :

C++

  1. #1
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut utiliser une fonction d une class dans une autre
    bonjour,

    voila j ai un petit problème dans le cadre d un cour de c++
    sur le polymorphisme.

    mon problème est le suivant :

    mon professeur m a demandé d utiliser ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Base * Derived::Allocate()
    {
        Derived *Test;
        Test = new Derived (*this);
        return Test;
    }
    dans toutes les class enfant de la class Base qui, elle, est une une class abstraite.

    donc je dois mettre la fonction qui se trouve ici au dessus, dans une class qui elle se trouve

    dans un autre header (.h) que la class dans la quelle je dois la mettre.

    toutes les class, il y en a 4 doivent contenir cette fonction telle quelle

    mais je n y arrive pas.

    quelqu'un aurait il une solution s il vous plait ?

    merci d avance

  2. #2
    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,

    C'est pourtant la base même du polymorphisme !!!

    Avant de te donner la solution (ce serait décidément trop simple ), je ne peux que te conseiller de relire ton cours car je serais vraiment étonné que la manière de le faire n'apparaisse pas en toute lettre

    Sinon, renseignes toi sur internet sur des termes comme "fonction virtuelle" voire "fonction virtuelle pure" (au niveau de la classe de base).

    Reviens vers nous en nous présentant un code un peu plus complet, et, au besoin, nous te redonneront un mini cours sur le sujet

  3. #3
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    et bien j avais déjà relu mon cour,

    et j avais mis ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    CBase * CDerived::Allocate()
    {
        CDerived *pTemp;
        pTemp = new CDerived (*this);
        return pTemp;
    }
    dans chaque enfants de CBase c est a dire :

    CDerived, CSetType, Cpoint et CComplexe

    mais j ai un probleme au niveau du CDerived que CSetType.h ne semble pas trouver alors que j ai bien mi :
    virtual CBase * Allocate() = 0; dans CBase.h

    que les deux fonctions sont bien enfants de CBase car j ais mis :
    : public CBase dans CSetType.h et dans CDuplicate.h

    et que j ais bien mis la fonction allocate dans la patie public dans les 2 enfants.
    que se soit dans CSetType.h ou dans CDuplicate.h

    mais il me reste un problème.

    donc, pour le moment j ais ceci :
    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
     
    #include "CBase.h"
     
    class CPoint : public CBase
    {
    	private :
    		int m_Adresse1;
    		int m_Adresse2;
    	public :
    		void Affiche();
     
    	friend CBase * CDerived::Allocate();
     
    };
     
    CBase * CDerived::Allocate()
    {
    	CDerived *pTemp;
    	pTemp = new CDerived (*this);
    	return pTemp;
    }
    ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class CBase
    {
    	public :
    		virtual void Affiche() = 0;
    		virtual CBase * Allocate() = 0;
    };
    et ceci :



    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
     
    #include "CBase.h"
     
    class CDerived  : public CBase
    {
    	public :
     
    		CBase * CDerived::Allocate()
    		{
    			CDerived *pTemp;
    			pTemp = new CDerived (*this);
    			return pTemp;
    		}
     
    		void Affiche();
    };
    j ais quand même passé un certain temps, avant d aller voir la réponse sur le net.

    mais je vais encore aller revoir mon cour, j ais surement oublie quelque chose.

  4. #4
    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
    Oui, tu as manifestement oublié une ou deux choses

    D'abord, au niveau de la résolution des noms.

    Lorsque l'on définit une fonction en dehors de la classe dans laquelle elle est déclarée, la sytaxe est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <type de retour> <nom de la classe>:: <nom de la fonction>(<paramètres éventuels>) <const éventuel>{
       /* contenu de la fonction */
    }
    Ensuite, le fait que le but des fonctions virtuelles est de permettre de les redéfinir dans les classes dérivées : tu les déclares comme virtuelles (éventuellement pure s'il n'y a pas moyen de donner un comportement par défaut) dans la classe de base, puis tu les déclares à nouveau dans chaque classe pour laquelle tu dois la définir.

    Et, bien sur, tu n'oublies pas de fournir la définition qui va avec (de préférence dans le fichier d'implémentation)

    Allez, encore un effort, et, si tu n'y arrives vraiment pas, je me verrai bel et bien contraint d'écrire un petit roman

  5. #5
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    ok, merci.

    je vais revoir tout ça avec le complément d information, que tu viens de me fournir.

  6. #6
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    ben, en gros il me semble, que c est ce que j ais fais.

    mais mon probleme, est que le prof ne veux pas que j ecrive ceci dans CPoint

    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
    #include "CBase.h"
     
    class CPoint : public CBase
    {
    	private :
    		int m_Adresse1;
    		int m_Adresse2;
    	public :
    		void Affiche();
     
    	CBase * CPoint::Allocate()
    	{
    		CDerived *pTemp;
    		pTemp = new CDerived (*this);
    		return pTemp;
    	}
    };
    car, je pense, qu'il faut que je me serve de la fonction qui est écrite dans CDerived.

  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
    J'ai dit:
    Citation Envoyé par moi meme
    Lorsque l'on définit une fonction en dehors de la classe dans laquelle elle est déclarée, la syntaxe est

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <type de retour> <nom de la classe>:: <nom de la fonction>(<paramètres éventuels>) <const éventuel>{
       /* contenu de la fonction */
    }
    On peut définir une fonction directement dans la classe à laquelle elle appartient, mais:
    1. la fonction est alors considérée comme implicitement inline (ce qui n'est, malgré tout, pas très compatible avec la notion de virtualité)
    2. il ne faut pas faire précéder le nom de la fonction du nom de la classe

    Si tu veux définir le comportement d'une fonction membre dans la définition de la classe, la syntaxe est du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class MaClass{
    /* public: ou private: ou protected: */
        <type de retour> <nom de la fonction>(<paramètres éventuels>)<const éventuel>{
            /* ce qui doit être fait */
        }
    }
    Allez, encore un essai
    PS: Lorsque tu donnes du code, je te serait reconnaissant d'utiliser la balise adéquate (le bouton # qui se trouve en haut du formulaire), afin qu'il soit représenté de manière correcte (cela fait trois fois que je modifie un de tes messages )

  8. #8
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    ah, ok
    je pensais que cela ce faisait automatiquement
    j y penserai pour la prochaine fois.

    encore désolé, mais je suis nouveau sur le site.
    et je ne programme pas depuis longtemps comme tu as pu le constater.

  9. #9
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    euh désolé d insisté,

    mais mon but n est pas d écrire une fonction membre.
    et de plus, il est bien écrit dans l énoncé :

    Dans chacune des classes dérivées de CBase, on va trouver une implémentation de la fonction Allocate() :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    CBase * CDerived ::Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }

  10. #10
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    La syntaxe que tu montres est clairement celle de la définition d'une fonction membre!
    les fonctions définies avec la syntaxe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    type_retour QuelqueChose::nomFonction(args){
       /*code*/
    }
    Sont par définition des fonctions membres de QuelqueChose...et si QuelqueChose est une classe, alors c'est la définition d'une fonction membre d'une classe.

  11. #11
    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 geocarre27 Voir le message
    euh désolé d insisté,

    mais mon but n est pas d écrire une fonction membre.
    et de plus, il est bien écrit dans l énoncé :

    Dans chacune des classes dérivées de CBase, on va trouver une implémentation de la fonction Allocate() :
    oui, une implémentation de Allocate qui est propre et spécifique à la classe dérivée dans laquelle tu la déclares / pour laquelle tu la définis, à savoir CDerived , CSetType, Cpoint ou CComplexe.

    Ainsi, le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    CBase * CDerived ::Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }
    n'est il valable que pour la classe ... CDerived parce que:
    1. Le nom de la classe que tu indique est CDerived
    2. Tu t'attends, en invoquant Allocate() depuis un CPoint (par exemple) à obtenir un pointeur vers un objet dont le type réel est CPoint et non CDerived (et ce, même s'il "passe pour etre" un pointeur sur Base)

    Il faut donc que tu adaptes ce code au type réel des différentes classes dérivées

  12. #12
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    oui c est bien ce que j ai pense en premier,

    et j ai poser la question à mon prof.

    j ai demander dois je utiliser ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    CBase * CDerived ::Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }
    ou ceci dans CPoint :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    CBase * CPoint ::Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }
    il ma clairement répondu qu il fallait utiliser la 1ere solution à savoir

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    CBase * CDerived ::Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }
    et il m a ensuite dit, que la fonction allocate était une fonction virtual.

  13. #13
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    j imagine, que ceci n est pas bon non plus dans CPoint :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    CBase * Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }

  14. #14
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Citation Envoyé par geocarre27 Voir le message
    j imagine, que ceci n est pas bon non plus dans CPoint :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    CBase * Allocate()
    {
     
    	CDerived *pTemp ;
     
    	pTemp = new CDerived (*this) ;
     
    	return pTemp ;
    }
    L'usage correct pour implémenter une fonction virtuelle dans des classes dérivées, c'est de définir CDerived::Allocate() dans CDerived, CSetType::Allocate() dans CSetType, et ainsi de suite. Par ailleurs, la définir pareil (retournant un CDerived) dans toutes les classes n'est pas à proprement parler incorrect, mais ça ne rime pas à grand chose...

  15. #15
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    ben je n ais jamais dit que ça devait rimer à quelque chose.

    mais mon prof m a demandé de faire comme cela et je n y arrive pas.

    bien sure, avec ta façon j y arrive très bien, j avais déjà fais un exercice comme celui auparavant, et le prof avait dit qu il était correcte.

    mais ce n est pas ce qu il m a demande cette fois ci.

    si tu sais comment faire, je suis preneur.

    merci d avance.

  16. #16
    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 therwald Voir le message
    L'usage correct pour implémenter une fonction virtuelle dans des classes dérivées, c'est de définir CDerived::Allocate() dans CDerived, CSetType::Allocate() dans CSetType, et ainsi de suite. Par ailleurs, la définir pareil (retournant un CDerived) dans toutes les classes n'est pas à proprement parler incorrect, mais ça ne rime pas à grand chose...
    Et encore!!!

    Au cas où tu ne l'aurais pas remarqué, l'invocation de new se fait sous la fome de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    pTemp = new CDerived (*this) ;
    autrement dit, une référence, a priori constante (mais bon, comme allocate n'est pas une fonction constante dans le cas présent, on va faire main basse sur le sujet ) sur un objet dont le type peut, très bien etre:
    • CBase ou CDerived dans CDerived ::Allocate()
    • CBase ou CSetType dans CSetType::Allocate()
    • CBase ou Cpoint dans Cpoint::Allocate()
    • CBase ou CComplexe dans CComplexe::Allocate()
    Si le constructeur invoqué prend la forme de CDerived(CBase /* const */ & b) afin d'instancier le membre correspondant.

    Il n'y aura pas forcément de problème: on se trouve (plus ou moins) dans le cadre de l'utilisation du patron de conception "décorateur".

    Mais, si c'est le type dérivé... aie ouille ouille aie aie

    Cela implique que la classe CDerived devra avoir une connaissance de toutes les autres classes dérivées ca sent pour le moins mauvais en terme de conception.

    Sinon, il reste une dernière solution:

    Le fait que CSetType, Cpoint et CComplexe puissent dériver, non pas de CBase, mais de ... CDerived et que le constructeur prenne une référence (éventuellement constante) sur un objet de type CDerived afin d'instancier le membre correspondant.

    Comme on se trouve alors dans un cadre fort proche du patron de conception "décorateur", on sera alors confronté au problème du fait que l'on en arrive (si la référence est constante) à autoriser la copie de l'objet, ce qui n'est pas forcément la meilleure des choses à faire

    Mais, cette dernière solution n'est, visiblement, pas envisagée

    Or, si le prof souhaite que toutes les fonctions allocate renvoient un pointeur pointant sur un nouvel objet de type CDerived tout en étant redéfinies spécifiquement pour chaque type dérivé, nous sommes bel et bien dans la deuxième solution (il faut un constructeur de CDerived prenant une référence sur chaque type dérivé).

    Et alors là, comme je l'ai dit plus tot, il va y avoir des problèmes car, pour pouvoir travailler avec les types dérivés, il faut que le membre qui sera initialisé soit du type dérivé (autrement, il n'aurait servi à rien rendre ce comportement virtuel), soit une référence, soit un pointeur, soit une instance.

    Sauf que voilà: une classe ayant sémantique d'entité ne devrait pas être copiable (en plus, cela signifie qu'il serait aussi possible de placer chaque type dérivé dans un état reconnu comme "invalide")... Adieu l'idée d'utiliser une instance, car, pour pouvoir la créer, il faudrait copier l'objet reçu en paramètre comme membre de la classe CDerived.

    De plus, je me refuse (c'est une question de principe ) à initialiser un pointeur sur base de l'adresse d'une référence car on ne sais jamais jusqu'à quand l'objet qui a servi à la référence sera disponible... Adieu l'idée d'utiliser un pointeur comme membre de la classe CDerived.

    Enfin, les références doivent être initialisée au moment de leur création... Adieux l'idée d'utiliser une référence comme membre de la classe CDerived.

    Mais bon, continuons le raisonnement jusqu'au bout...

    Peut être l'idée est elle d'initialiser l'objet de type CDerived avec des données issues des autres types dérivés

    Mais, dans ce cas, il y a deux solutions:

    Soit il existe un (voir des) comportement(s) commun(s) à tous les types dérivés (éventuellement redéfini(s) pour chaque type dérivé) qui permet(tent) de récupérer les valeurs qui nous intéressent, soit il faut utiliser un comportement spécifique à chaque type dérivé pour pouvoir le faire.

    Dans le premier cas, Pourquoi ne pas déclarer ce comportement comme virtuel pur dans la classe de base

    Seulement, voilà... Encore une fois, il n'est plus nécessaire de redéfinir cette fonction allocate dans tous les types dérivés: une fonction (non virutelle) déclarée (et définie) uniquement dans Base est amplement suffisante

    Et, dans tous les cas, pourquoi se faire ch..er à transmettre une référence sur le type particulier et à nous "coltiner" une dépendance supplémentaire entre la classe CDerived et toutes les autres classes dérivées

    Pourquoi ne pas créer un constructeur de CDerived qui prenne directement les valeurs (par référence éventuellement constante si besoin ) qui l'intéressent et faire en sorte que les fonction Allocate de chaque type dérivé fournissent directement ces valeurs

    Bref:
    @therwald je suis pour le moins mitigé quand tu dis que ce n'est pas à proprement parler "incorrect"... J'aurais tendance à dire que, bien que cela puisse être correct en terme de code, c'est totalement incorrect du point de vue conceptuel .

    Et mon avis personnel est que les gens éviteraient énormément de problèmes s'ils prenaient l'habitude de réfléchir un tout petit peu en terme de conception avant de réfléchir en terme de code car rien ne pourra jamais faire qu'une mauvaise décision conceptuelle devienne valable "par la simple magie du code" .

    (si tu veux que je précise ce point de vue, il vaudrait mieux ouvrir une nouvelle discussion, car je suppute par expérience que ca risque d'être un débat animé ).

    @geocarre27 : Je vais taire ce que je pense réellement de ton prof, ca risquerait de le rendre nerveux.

    Mais je te conseillerais bien volontiers de lui faire une réponse "diplomatique" du genre de "désolé monsieur, mais ce que vous me demandez ne rime à rien", quitte à réutiliser mes arguments voire, à lui donner le lien de cette discussion (raison pour laquelle je reste moi meme diplomate à son sujet )

    De plus, même si ce n'est qu'un détail, je tiens quand même à préciser que j'ai déjà beaucoup de mal à envisager un quelconque point commun entre les classes CDerived, CSetType, Cpoint et CComplexe qui justifieraient qu'elles héritent toutes de CBase

    Je suis réellement tout près à crier "attention!! tu te diriges tout droit vers un god object, et ce n'est vraiment pas l'idéal".

    D'ailleurs, je le crie bien fort .

    Ainsi, des données de type CPoint ou CComplexe auront, typiquement, sémantique de valeur, ce qui est fortement incompatible avec le fait de les intégrer dans une hiérarchie de classes.

    J'aurais en outre tendance (d'après le nom ) à estimer que CSetType serait, typiquement, un genre de collection quelconque et, bien que les collections soient souvent "à mi chemin" entre une sémantique d'entité et une sémantique de valeur, il n'est pas meilleur de vouloir les introduire dans une hiérachie de classes.

    Ne serait-ce que parce que les règles de la programmation par contrat font que l'on ne peut décemment pas estimer qu'une liste triée puisse hériter d'une liste (non triée)

    Au final, la seule classe qui pourrait, éventuellement (et encore: uniquement parce que je n'en sais pas d'avantage pour l'instant ) hériter de CBase, ce serait... CDerivee

  17. #17
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2013
    Messages : 14
    Points : 3
    Points
    3
    Par défaut
    ben je met plutôt ça sur deux choses .

    1) je n ais pas bien compris ce que le prof voulait me dire. étant donné comme d habitude, qu il laisse pas mal de place au sous entendus et a différentes solutions ce qui est logique je pense quand on fait un graduat.

    2) vu que j ais eu du mal à comprendre ce qu il voulait, j ais du mal exprimer ma question. et de plus pour respect pour son cour , je n ais pas mis la question entière qui fait plus de deux pages.

    car je vois mal comment un professeur qui bosse depuis des dizaines d années dans le milieu et qui est architecte programmeur au siège d une grosse multinational à Bruxelles ferait une tel erreur sur un cour qu il donne depuis un certain temps.

    tandis que moi je débute tant en programmation qu'en informatique et il y a des chances que je n ais pas compris sa question.

    je ne suis pas d habitude à remettre la faute sur moi, mais là je ne voie pas d alternative. j ai du mal comprendre c est tout.

    en tout cas je vous remercie d avoir pris du temps pour me répondre.

  18. #18
    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 geocarre27 Voir le message
    ben je met plutôt ça sur deux choses .

    1) je n ais pas bien compris ce que le prof voulait me dire. étant donné comme d habitude, qu il laisse pas mal de place au sous entendus et a différentes solutions ce qui est logique je pense quand on fait un graduat.

    2) vu que j ais eu du mal à comprendre ce qu il voulait, j ais du mal exprimer ma question. et de plus pour respect pour son cour , je n ais pas mis la question entière qui fait plus de deux pages.
    Ca, c'est toujours possible, et très clairement envisageable
    car je vois mal comment un professeur qui bosse depuis des dizaines d années dans le milieu et qui est architecte programmeur au siège d une grosse multinational à Bruxelles ferait une tel erreur sur un cour qu il donne depuis un certain temps.
    Là dessus, on peut par contre discuter.

    Tu pars, et c'est tout à ton honneur, d'un a priori bien favorable, et tu as sans doute raison de manière générale de partir de cet a priori.

    Cependant, j'ai beau savoir qu'il s'agit d'un exercice, mais je trouve par exemple le noms des classes particulièrement malheureux.

    CBase et CDerived, par exemple, sont des termes beaucoup trop génériques, qui nous empêche d'avoir une compréhension sémantique (au sens stricte s'entend) de ce que l'on attend de la part de ces classes.

    Or, la première question à se poser lorsqu'on envisage un héritage publique, avant meme de savoir si LSP est respecté, est
    puis-je réellement dire qu'un objet de type dérivé EST-UN objet du type de base
    Avec des termes comme CBase ou CDerived, strictement tout et n'importe quoi peut etre considéré comme étant un objet du type de base, voire, du type dérivé.

    Cela aura pour résultat de nous inciter à créer des classes de type "god objet": des classes dont la responsabilité est à ce point mal définie qu'on fini par les rendre responsables de strictement tout et n'importe quoi.

    Le principal problème que cela occasionne à plus ou moins long terme, c'est que tout finit par hériter, de manière directe ou indirecte, de cette classe de base.

    Si l'on veut profiter de l'héritage multiple (créer, par exemple, un turbo-générateur qui hérite d'une classe turbo et d'une classe générateur), on se retrouve alors dans une situation d'héritage en diamant (ou en losange) particulièrement embêtante car, à moins d'avoir eu recours à l'héritage virtuel (et donc d'être confronté à tous les problèmes que ce genre d'héritage implique), on se retrouve avec deux composantes de la classe de base: la première nous venant de la classe turob, la seconde nous venant de la classe générateur.

    Ensuite, vient la classe CPoint.

    Etant donné que l'on a si peu de référence sémantique au sujet des classes CBase et CDerived, il est tout à fait possible d'estimer qu'un point EST-UN objet de type CBase, tout comme il est tout à fait possible d'estimer qu'un point EST-UN objet de type CDerived.

    A partir de là, le principe de substitution de Liskov ne nous sauvera plus, car, étant donné que l'on attend de la part de la classe CPoint qu'elle fournisse le service "Affiche" et un service Allocate, et que ce sont des services que l'on a déjà déclaré pour la classe CBase (et donc, par extension, à la classe CDerived), on peut effectivement considérer que
    Toute propriété valide de la classe CBase est une propriété valide de la classe Point
    Seulement, voilà...

    Ce n'est pas parce que deux classes proposent une interface publique similaire (dans le cas présent, les fonctions Affiche et Allocate) que ces deux classes ont forcément un quelconque lien de parenté directe ou indirecte.

    C'est d'autant plus vrai que les classes qui interviennent dans une hiérarchie de classe ont, typiquement, sémantique d'entité.

    Ces classes présentent les caractéristiques suivantes:
    1. Elle ne sont ni copiables ni assignable: tu ne voudrais pas que, parce que ton numéro de compte en banque a été utilisé deux fois, le payement effectué par celui qui dispose du même numéro de compte soit débité de ton compte à toi
    2. Elles sont identifiables de manière "unique et non ambigüe": il y a souvent une ou plusieurs information(s) qui permet(tent) d'identifier de manière strictement unique chaque instance de la classe parmi les autres (le numéro du compte, par exemple )
    3. Elles sont globalement modifiables: si on excepte les données qui permettent de les identifier, il est généralement possible de manipuler certaines données sans pour autant obtenir une nouvelle instance de la classe (on peut augmenter ou diminuer le solde du compte par exemple)
    4. Elles ne sont pas comparables: on peut envisager de comparer chaque donnée à laquelle on a accès (le numéro du compte ou le solde, par exemple) de manière séparée, mais il n'y a aucun intérêt à comparer "un compte à un autre compte" de manière globale
    5. Elles n'ont aucun besoin des opérateur arithmétiques: que pourrait bien signifier "incrémenter" un compte en banque
    Or, la classe point a, typiquement, sémantique de valeur.

    Ce sont des classes qui présentent les caractéristique suivantes:
    • Elle sont parfaitement copiables et assignables : il n'y a aucun problème à avoir, à un instant T, deux instances totalement distinctes d'un point présentant des valeurs identiques
    • Elles sont au minimum comparable par égalité: il est tout à fait possible de vérifier si deux points sont égaux en vérifiant si leurs valeurs x et y respectives sont égales (et l'on pourrait meme envisager de les comparer en inégalité, sous réserve d'assurer un état unique)
    • elles sont généralement constantes: si tu modifies un tant soit peu la valeur de l'abscisse ou de l'ordonnée d'un point, tu obtiens... un tout autre point
    • Il n'est pas exclu d'envisager de fournir des opérateurs arithmétiques, bien qu'il faille alors que leur sémantique soit clairement définie (par des conventions, peut etre )
    • Mais, surtout, elles n'ont pas vocation à s'intégrer dans une hiérarchie de classe, que ce soit en tant que classe de base ou en tant que classe dérivée: Tu l'auras compris, un point ne dispose d'aucune des caractéristiques qui permettrait de le considérer comme une classe ayant sémantique d'entité, il n'y a donc aucune raison de le faire intervenir dans une hiérarchie de classe


    La classe CComplexe quant à elle me pose un problème auquel tu n'es (je l'espère) pas confronté: le manque de précision quant au contexte dans laquelle elle sera utilisée

    S'agit-il d'une classe sensée représenter des nombres complexes, avec leur partie réelle et leur partie imaginaire, ou plutôt d'une classe qui devient tout simplement plus complexe du fait de sa destination et de son usage

    Dans le premier cas, cette classe tombe dans la même situation que la classe point : elle a sémantique de valeur et ne devrait pas intervenir dans une hiérarchie de classe

    Dans le second cas, elle rejoint CBase et CDerived dans le lot des classes pour lesquelles le nom choisi est bien trop imprécis pour représenter quelque chose

    Et enfin, il y a le cas de CSetType.

    Si l'on en juge par le nom, cette classe correspond sans doute à une collection d'objets quelconques.

    Les collections d'objets sont beaucoup plus proches des classes ayant sémantique de valeur que des classes ayant sémantique d'entité, bien qu'elles soient le plus souvent non constantes: ce n'est pas parce que tu rajoutes ou que tu retires un objets d'une collection que tu obtiens une autre collection

    Quoi qu'il en soit, une collection ne devrait pas non plus rentrer dans une hiérarchie de classes

    Comprenons nous bien: il ne s'agit nullement de dénigrer d'une quelconque manière le cours que ton prof te donne, ni même de mettre d'une quelconque manière sa compétence en doute.

    Simplement, on ne peut s'empêcher de se rendre compte qu'il est, malheureusement, une victime supplémentaire de "l'apprentissage historique" de la programmation orientée objet (et du C++ en particulier) et qu'il n'a peut etre "simplement" pas eu l'occasion de corriger le tir.

    J'espère instamment qu'il garde malgré tout l'esprit ouvert et qu'il est disposé, s'il voit cette discussion, à se remettre en question sur les points que j'ai abordés ici

    Mais je trouve dommage qu'au final, cette approche historique de la programmation OO continue à se répandre et à donner de mauvaises habitudes à ceux qui sont pour l'instant en formation

  19. #19
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Citation Envoyé par koala01 Voir le message
    @therwald je suis pour le moins mitigé quand tu dis que ce n'est pas à proprement parler "incorrect"... J'aurais tendance à dire que, bien que cela puisse être correct en terme de code, c'est totalement incorrect du point de vue conceptuel .

    Et mon avis personnel est que les gens éviteraient énormément de problèmes s'ils prenaient l'habitude de réfléchir un tout petit peu en terme de conception avant de réfléchir en terme de code car rien ne pourra jamais faire qu'une mauvaise décision conceptuelle devienne valable "par la simple magie du code" .

    (si tu veux que je précise ce point de vue, il vaudrait mieux ouvrir une nouvelle discussion, car je suppute par expérience que ca risque d'être un débat animé ).
    Le débat ne sera pas animé en ce qui me concerne, car je suis d'accord avec toi: un code syntaxiquement correct mais conceptuellement boiteux je le considère comme boiteux tout court. En relisant, je reconnais avoir concédé un peu vite que c'était du code correct, plutôt par soucis de modération dans mes propos. J'ai cependant souvenir d'un certain débat sur le sens de la dérivation...

  20. #20
    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 therwald Voir le message
    Le débat ne sera pas animé en ce qui me concerne, car je suis d'accord avec toi: un code syntaxiquement correct mais conceptuellement boiteux je le considère comme boiteux tout court. En relisant, je reconnais avoir concédé un peu vite que c'était du code correct, plutôt par soucis de modération dans mes propos. J'ai cependant souvenir d'un certain débat sur le sens de la dérivation...
    Des débats basés sur le problème du respect de LSP, il y en a eu quelques uns, dont celui-ci qui a, entre autres, fortement justifié la partie que tu cites

Discussions similaires

  1. appel d'une classe dans une fonction
    Par fraisa1985 dans le forum Général Java
    Réponses: 7
    Dernier message: 27/03/2008, 12h11
  2. Comment créer une liste ou une instance de classe dans une fonction ?
    Par Neolander dans le forum Général Python
    Réponses: 9
    Dernier message: 05/03/2008, 19h22
  3. [POO] appeler une methode de classe dans une fonction
    Par ryykko dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 11/02/2008, 22h10
  4. Réponses: 40
    Dernier message: 21/06/2007, 17h58
  5. [.NET2.0][C#]Passage type de classe dans une fonction
    Par SLE dans le forum Windows Forms
    Réponses: 4
    Dernier message: 06/06/2006, 15h48

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