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 :

Problème de polymorphisme dynamique


Sujet :

C++

  1. #1
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut Problème de polymorphisme dynamique
    Bonjour à tous,

    Je dois implémenter une interface et la tester. Comme mon problème est difficile à expliquer et à comprendre sous forme de prose, je vous le soumets sous forme d'un code prenant l'exemple d'un garage :

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
     
     
    #etc...
    // Mon interface
    class InterfaceVoiture
    {
    public :
     
    	void InterfaceVoiture();
     
    	virtual void accelerer() = 0;
     
    	virtual void freiner() = 0;
     
    	virtual int getVitesseMax() = 0;
     
    	virtual std::string getMarque() = 0;
    }
     
    // Mon implémentation
    class Voiture : public InterfaceVoiture
    {
    protected :
     
    	int vitesseMax;
     
    	std::string marque;
     
    public :
     
    	Voiture() :
    		vitesseMax{130},
    		marque{"Peugeot"}
     
    	virtual void accelerer();
     
    	virtual void freiner();
     
    	virtual int getVitesseMax();
     
    	virtual std::string getMarque();
    }
     
    // Une autre classe devant agréger mon implémentation
    class Garage
    {
    protected :
     
    	// Ici je suis obligé de fournir l'interface et non l'implémentation :
     
    	// Cela permet à garages de contenir des classes de test triviales (des mocks) de voitures
    	// et cela fait partie du cahier des charges...
    	std::vector<InterfaceVoiture*> voitures;
     
    	...
     
    public :
     
    	// Même remarque que ci-dessus 
    	InterfaceVoiture* getVoitureAt(int index){return voitures.at(index);}
     
    	...
     
    }
     
    // Ma classe de test de l'implémentation
    class TestVoiture 
    {
    protected :
     
    	Garage* garage;
     
    	// Je dois tester la classe voiture en créant d'abord un garage
    	Voiture* voitureATester;
     
    public :
     
    	void testerVoiture(int index)
    	{
                    garage = new Garage();
                    voitureATester = new VoitureATester();
     
    		// Malheureusement à cause des contraintes le getter du garage est une InterfaceVoiture* et non une Voiture*...
    		voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index));
     
    		// Évidemment, une interface ne possède pas d'attribut, donc les getters ci-dessous renvoient une SEH exception,
    		// i.e. un pointeur non initialisé...
    		std:: cout << voitureATester->getVitesseMaxVoiture() << std::endl; // 130
     
    		std::cout << voitureATester->getMarqueVoiture() << std::endl; // Peugeot
    	}
    }
    Comment obtenir la vitesse maximale de la voiture et sa marque sachant :

    - Que l'on doit obtenir ces valeurs en créant une voiture contenue dans un garage créé dans la classe de test
    - Que le getter permettant d'obtenir une voiture dans la classe "Garage" doit être une interface ?

    Inutile de remarquer certaines horreurs telles que l'initialisation des attributs de la voiture à 130 ou "Peugeot" directement, je l'ai fait pour simplifier.

    Merci d'avance pour vos réponses !

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Qu'est censé retourner getVoitureAt() quand la collection est vide?
    Et pourquoi de pas directement déclarer voitureATester avec le type InterfaceVoiture?

    PS: La voiture allouée à la ligne 81 est leakée par le code actuel.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Bonjour, merci pour votre prompte réponse !

    Citation Envoyé par Médinoc Voir le message
    Qu'est censé retourner getVoitureAt() quand la collection est vide?
    Dans mon "vrai" code, je renvoie une exception... Mais c'était pour simplifier le problème car si j'écris tout, cela devient trop long...

    Citation Envoyé par Médinoc Voir le message
    Et pourquoi de pas directement déclarer voitureATester avec le type InterfaceVoiture?
    En fait, je dois tester une instance de la classe Voiture... J'ai tout de même essayé de déclarer voitureAtester en tant qu'InterfaceVoiture :

    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
     
    class TestVoiture
    {
    protected :
    	InterfaceVoiture* voitureATester;
    	Garage* garage;
    	...
     
    public :
    	void testerVoiture(int index)
    	{
                    garage = new Garage();
    		voitureATester = new Voiture(); // La voiture à tester doit être une Voiture et non une InterfaceVoiture car je dois tester 
    		// la classe Voiture et ses getters ci-dessous...
     
    		voitureATester = dynamic_cast<Voiture*> garage->getVoitureAt(index);
     
    		std:: cout << voitureATester->getVitesseMaxVoiture() << std::endl; // 130
     
    		std::cout << voitureATester->getMarqueVoiture() << std::endl; // Peugeot
    	}
    }

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    S'il te plait, poste au moins le code complet de la classe TestVoiture, parce que pour l'instant les extraits que tu as postés défient la logique (fuites mémoire, objets re-créés sans raison apparente, etc. ).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    S'il te plait, poste au moins le code complet de la classe TestVoiture, parce que pour l'instant les extraits que tu as postés défient la logique (fuites mémoire, objets re-créés sans raison apparente, etc. ).
    Pardon... J'ai remis la déclaration de voitureATester en tant que Voiture* car j'ai besoin de ses attributs pour attributs pour appeler les getters getVitesseMaxVoiture() et getMarqueVoiture()...

    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
    26
    class TestVoiture 
    {
    protected :
     
    	Garage* garage; 
    	// Je dois tester la classe voiture en créant d'abord un garage
    	Voiture* voitureATester;
     
    public :
     
    	void testerVoiture(int index)
    	{
    		garage = new Garage();               
     
    		// Malheureusement à cause des contraintes le getter du garage est une InterfaceVoiture* et non une Voiture*...
    		voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index)); 
     
    		// Évidemment, InterfaceVoiture ne possède pas d'attribut, donc les getters ci-dessous renvoient une SEH exception,
    		// i.e. un pointeur non initialisé...
    		std:: cout << voitureATester->getVitesseMaxVoiture() << std::endl; // 130
     
    		std::cout << voitureATester->getMarqueVoiture() << std::endl; // Peugeot
     
    		delete garage; // La voiture sera détruite à l'appel du destructeur du garage		
    	}
    }
    Rappel du code testé :

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #etc...
    // Mon interface
    class InterfaceVoiture
    {
    public :
     
    	void InterfaceVoiture();
     
    	virtual void accelerer() = 0;
     
    	virtual void freiner() = 0;
     
    	virtual int getVitesseMax() = 0;
     
    	virtual std::string getMarque() = 0;
    }
     
    // Mon implémentation
    class Voiture : public InterfaceVoiture
    {
    protected :
     
    	int vitesseMax;
     
    	std::string marque;
     
    public :
     
    	Voiture() :
    		vitesseMax{130},
    		marque{"Peugeot"}
     
    	virtual void accelerer();
     
    	virtual void freiner();
     
    	virtual int getVitesseMax();
     
    	virtual std::string getMarque();
    }
     
    // Une autre classe devant agréger mon implémentation
    class Garage
    {
    protected :
     
    	// Ici je suis obligé de fournir l'interface et non l'implémentation :
     
    	// Cela permet à garages de contenir des classes de test triviales (des mocks) de voitures
    	// et cela fait partie du cahier des charges...
    	std::vector<InterfaceVoiture*> voitures;
     
    	...
     
    public :
     
    	// Même remarque que ci-dessus 
    	InterfaceVoiture* getVoitureAt(int index){return voitures.at(index);}
     
    	...
     
    }

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Ton code devrait péter dès ce point:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    		garage = new Garage();               
     
    		// Malheureusement à cause des contraintes le getter du garage est une InterfaceVoiture* et non une Voiture*...
    		voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index));
    Parce que tu fais un getVoitureAt() sur un garage vide, et donc le Garage::voitures lance une std::out_of_bounds...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Ton code devrait péter dès ce point:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    		garage = new Garage();               
     
    		// Malheureusement à cause des contraintes le getter du garage est une InterfaceVoiture* et non une Voiture*...
    		voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index));
    Parce que tu fais un getVoitureAt() sur un garage vide, et donc le Garage::voitures lance une std::out_of_bounds...
    Je poste le code de la classe de test en prenant en compte vos remarques :

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    #etc...
    // Mon interface
    class InterfaceVoiture
    {
    public :
     
    	void InterfaceVoiture();
     
    	virtual void accelerer() = 0;
     
    	virtual void freiner() = 0;
     
    	virtual int getVitesseMax() = 0;
     
    	virtual std::string getMarque() = 0;
    }
     
    // Mon implémentation
    class Voiture : public InterfaceVoiture
    {
    protected :
     
    	int vitesseMax;
     
    	std::string marque;
     
    public :
     
    	Voiture() :
    		vitesseMax{130},
    		marque{"Peugeot"}
     
    	virtual void accelerer();
     
    	virtual void freiner();
     
    	virtual int getVitesseMax();
     
    	virtual std::string getMarque();
    }
     
    // Une autre classe devant agréger mon implémentation
    class Garage
    {
    protected :
     
    	// Ici je suis obligé de fournir l'interface et non l'implémentation :
     
    	// Cela permet à garages de contenir des classes de test triviales (des mocks) de voitures
    	// et cela fait partie du cahier des charges...
    	std::vector<InterfaceVoiture*> voitures;
     
    	...
     
    public :
     
    	// Même remarque que ci-dessus 
     
            Garage(){voitures.push_back(new Voiture());} // JE REMPLIS LE GARAGE ICI
     
    	InterfaceVoiture* getVoitureAt(int index){return voitures.at(index);}
     
    	...
     
    }
     
    // Ma classe de test de l'implémentation
    class TestVoiture 
    {
    protected :
     
    	Garage* garage;
     
    	// Je dois tester la classe voiture en créant d'abord un garage
    	Voiture* voitureATester;
     
    public :
     
    	void testerVoiture(int index)
    	{
                    garage = new Garage();
                    voitureATester = new VoitureATester();
     
    		// Malheureusement à cause des contraintes le getter du garage est une InterfaceVoiture* et non une Voiture*...
    		voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index));
     
    		// Évidemment, une interface ne possède pas d'attribut, donc les getters ci-dessous renvoient une SEH exception,
    		// i.e. un pointeur non initialisé...
    		std:: cout << voitureATester->getVitesseMaxVoiture() << std::endl; // 130
     
    		std::cout << voitureATester->getMarqueVoiture() << std::endl; // Peugeot
    	}
    }

    Je vous tiendrai au courant si cela fonctionne. Merci !

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    La source de l'erreur est simple: Ton garage contient (un pointeurs vers) une Voiture, et non pas une VoitureATester.
    Tu devrais remplir le garage dans ton code de test, plutôt que dans son constructeur.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut Ne jamais oublier les constructeurs de copie !
    Médinoc,

    Après avoir modifié mon code pour que le garage se remplisse d'une voiture à sa construction, je suis toujours tombé sur la SEH exception et j'ai continué à débugger.

    Je m'aperçois alors que voitureATester ne possède pas les mêmes attributs que garage->getVoitureAt(index)... En fait, j'ai oublié de définir un constructeur de copie pour la classe Voiture !!

    Je le définis alors, et j'écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    voitureATester = new Voiture(dynamic_cast<Voiture*>(garage->getVoitureAt(index)));
    Et voilà... Malgré le fait que voitureATester soit une Voiture*, on peut parfaitement retourner une InterfaceVoiture* pour getVoitureAt() dans le cadre des contraintes citées plus haut et le caster en Voiture* sans perdre des données ou attributs, contrairement à ce que je pensais.

    La perte d'attributs provenait du fait que le constructeur de copie par défaut n'était pas suffisant, d'où la nécessité de ne jamais oublier le constructeur par copie. Merci Médinoc pour ton aide !

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Euh, tes objets sont polymorphiques. Ils ne devraient même pas être copiables!
    Si tu compiles en C++11, tu devrais déclarer tes constructeurs de copie en =delete.
    Sinon, tu devrais les déclarer private sans les implémenter.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Euh, tes objets sont polymorphiques. Ils ne devraient même pas être copiables!
    Si tu compiles en C++11, tu devrais déclarer tes constructeurs de copie en =delete.
    Sinon, tu devrais les déclarer private sans les implémenter.
    Je ne suis pas sûr de bien comprendre... De quels objets parles-tu ? De la classe Voiture ? Et "=delete" permet de spécifier que la classe Voiture est non copiable, non ? En tout cas, même si cette classe est polymorphique, j'ai tout de même réussi à la cloner donc je ne comprends pas...

  12. #12
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Citation Envoyé par Jimmy91 Voir le message
    Je ne suis pas sûr de bien comprendre... De quels objets parles-tu ? De la classe Voiture ? Et "=delete" permet de spécifier que la classe Voiture est non copiable, non ? En tout cas, même si cette classe est polymorphique, j'ai tout de même réussi à la cloner donc je ne comprends pas...
    Il parle des objets que tu manipules, les instances de Voitures.
    Ton vecteur étant une collection de InterfaceVoiture*, tu ne devrais pouvoir manipuler ces objets que via cette interface. Dans le cas contraire (et c'est le cas), tu casses la "sémantique" du vecteur, de l'interface, et tu perds l'intérêt du polymorphisme.
    Une Voiture est une InterfaceVoiture, mais l'inverse n'est pas vrai. Ici, tu as testé avec une interface, et une classe en héritant : Voiture. C'est grâce (ou à cause) de ça que tu peux faire un dynamic_cast<> de InterfaceVoiture en Voiture, puisque toutes les instances de InterfaceVoiture sont, en réalité, des Voiture. Maintenant, si tu décides de créer une deuxième classe implémentant InterfaceVoiture, le dynamic_cast<> va échouer (puisque le type "réel" de l'instance n'est pas Voiture). Et pourtant, sémantiquement, ton objet est correct puisqu'il satisfait l'interface InterfaceVoiture ... Le problème n'est alors plus technique mais conceptuel.
    Comme on dit souvent, lorsqu'on a besoin de passer par un dynamic_cast<>, il y a un souci de conception.

  13. #13
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par the Hound Voir le message
    Une Voiture est une InterfaceVoiture, mais l'inverse n'est pas vrai. Ici, tu as testé avec une interface, et une classe en héritant : Voiture. C'est grâce (ou à cause) de ça que tu peux faire un dynamic_cast<> de InterfaceVoiture en Voiture, puisque toutes les instances de InterfaceVoiture sont, en réalité, des Voiture. Maintenant, si tu décides de créer une deuxième classe implémentant InterfaceVoiture, le dynamic_cast<> va échouer (puisque le type "réel" de l'instance n'est pas Voiture). Et pourtant, sémantiquement, ton objet est correct puisqu'il satisfait l'interface InterfaceVoiture ... Le problème n'est alors plus technique mais conceptuel.
    Comme on dit souvent, lorsqu'on a besoin de passer par un dynamic_cast<>, il y a un souci de conception.
    Bonjour the Hound,

    Merci pour ta réponse. Je suis bien d'accord pour affirmer qu'il y a un souci de conception comme tu le dis, mais le problème, c'est que je ne vois pas comment faire autrement. En effet, je dois implémenter l'InterfaceVoiture qui existait déjà dans une classe Voiture, puis tester cette classe Voiture. Le dynamic_cast que j'ai effectué est inclus dans le test de la classe Voiture, donc je ne risque pas de rencontrer un autre membre hérité de InterfaceVoiture a priori, ce qui résout le problème du dynamic_cast...

    J'avoue que je ne comprends pas bien le fait de ne tester que des InterfaceVoiture alors que je dois tester des Voiture...

  14. #14
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Tu ne dois pas "tester des voitures", tu dois tester si la classe Voiture implémente correctement InterfaceVoiture. Et donc pour cela, tu n'as besoin que d'y faire des appels à travers InterfaceVoiture.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  15. #15
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Hum, si tu ne manipules que des instances de Voiture, alors les éléments de ton vecteur doivent être des Voiture, et non des InterfaceVoiture.
    Maintenant, si tu est obligé de déclarer un vecteur de InterfaceVoiture, alors encore une fois, tu dois au possible te servir de cette interface pour manipuler ton objet.
    Je ne sais pas ce que tu veux faire exactement avec tes objets, mais d'après le code que tu nous as soumis, tu utilises des fonctions comme getVitesseMax() et getMarque(). Or ces fonctions sont définies dans InterfaceVoiture. Pourquoi ne pas les utiliser ?

  16. #16
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Tu ne dois pas "tester des voitures", tu dois tester si la classe Voiture implémente correctement InterfaceVoiture. Et donc pour cela, tu n'as besoin que d'y faire des appels à travers InterfaceVoiture.
    J'ai essayé de prendre en compte ce conseil dans le code de test ci-dessous (cf commentaires), c'est tout ce que j'ai trouvé pour "faire appel à Voiture à travers InterfaceVoiture". Est-ce correct ?

    Citation Envoyé par the Hound Voir le message
    Hum, si tu ne manipules que des instances de Voiture, alors les éléments de ton vecteur doivent être des Voiture, et non des InterfaceVoiture.
    Maintenant, si tu est obligé de déclarer un vecteur de InterfaceVoiture, alors encore une fois, tu dois au possible te servir de cette interface pour manipuler ton objet.
    Je ne sais pas ce que tu veux faire exactement avec tes objets, mais d'après le code que tu nous as soumis, tu utilises des fonctions comme getVitesseMax() et getMarque(). Or ces fonctions sont définies dans InterfaceVoiture. Pourquoi ne pas les utiliser ?
    En effet, je suis obligé de déclarer un vecteur d'InterfaceVoiture. J'ai modifié le code pour utiliser les fonctions définies dans InterfaceVoiture, mais est-ce que je me sers correctement de l'interface "pour manipuler mon objet" ? (cf commentaires)

    Le code de test modifié :

    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
    26
    27
    28
    29
    30
    31
    class TestVoiture
    {
    protected:
     
    	Garage* garage;
     
    	// Création de l'interface pour pouvoir tester si Voiture implémente bien InterfaceVoiture 
    	InterfaceVoiture* voitureATester;
     
    public:
     
    	void testerVoiture(int index)
    	{
    		garage = new Garage();
     
    		// Appel de la Voiture à travers InterfaceVoiture
    		voitureATester = new Voiture();		
    		voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index));
     
    		// Appel des méthodes getVitesseMax() et getMarque() énoncées par l'interface
    		std::cout << voitureATester->getVitesseMax() << std::endl; // returns 130
     
    		std::cout << voitureATester->getMarque() << std::endl; // returns "Peugeot"
    	}
     
    	void run()
    	{
    		testerVoiture(0);
    	}
     
    };
    Le reste du code :

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    class InterfaceVoiture
    {
    public:
     
    	InterfaceVoiture()
    	{
     
    	}
     
    	virtual int getVitesseMax() = 0;
     
    	virtual std::string getMarque() = 0;
    };
     
     
    class Voiture : public InterfaceVoiture
    {
    protected:
     
    	int vitesseMax;
     
    	std::string marque;
     
    public:
     
    	Voiture()
    	{
     
    	}
     
    	Voiture(int _vMax, std::string _marque) :
    		vitesseMax{ _vMax },
    		marque{ _marque }
    	{
     
    	}
     
    	virtual int getVitesseMax()
    	{
    		return vitesseMax;
    	}
     
    	virtual std::string getMarque()
    	{
    		return marque;
    	}
    };
     
    // Une autre classe devant agréger mon implémentation
    class Garage
    {
    protected:
     
    	// Ici je suis obligé de fournir l'interface et non l'implémentation :
     
    	// Cela permet à garages de contenir des classes de test triviales (des mocks) de voitures
    	// et cela fait partie du cahier des charges...
    	std::vector<InterfaceVoiture*> voitures;	
     
    public:
     
    	// Même remarque que ci-dessus 
     
    	Garage()
    	{ 
    		voitures.push_back(new Voiture(130,"Peugeot")); // JE REMPLIS LE GARAGE ICI
    	} 
     
    	InterfaceVoiture* getVoitureAt(int index){ return voitures.at(index); }	
     
    };
    Encore merci pour vos réponses, le test d'interfaces reste encore très flou pour moi.

    P.S. : Ce code compile est affiche bien la vitesse max et la marque de la voiture. Reste à savoir si ce code est correct dans le cadre d'un test d'implémentation d'interfaces !

  17. #17
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Citation Envoyé par Jimmy91 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    voitureATester = new Voiture();
    voitureATester = dynamic_cast<Voiture*>(garage->getVoitureAt(index));
    La première line est inutile : tu crées une nouvelle voiture, que tu leakes après (le pointeur voitureATester est réassigné pour contenir le résultat de ton dynamic_cast<>, donc la voiture que tu as nouvellement créée se retrouve perdue dans le tas).
    La deuxième ligne : c'est là où est ton problème.
    voitureATester est un InterfaceVoiture*.
    Garage::getVoitureAt() renvoie un InterfaceVoiture*.
    Pourquoi caster en Voiture* ? Si tout ce dont tu as besoin c'est d'appeler getVitesseMax() et getMarque(), qui sont définies dans InterfaceVoiture, le cast est inutile. Plus important, imaginons que je crée une classe Camion, qui implémente InterfaceVoiture ; alors si je mets ce camion dans le garage, le dynamic_cast<> va échouer, puisque un Camion n'est pas une Voiture. Par contre, sans le cast, Camion implémentant InterfaceVoiture, je pourrai tout à fait appeler getVitesseMax(), que mon objet soit en réalité un camion ou une voiture.

  18. #18
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    En gros, ces deux lignes peuvent être simplement remplacées par:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    voitureATester = garage->getVoitureAt(index);
    Qui lancera une exception std::out_of_bounds pour toute valeur d'index autre que zéro.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  19. #19
    Membre actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Juillet 2014
    Messages
    103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2014
    Messages : 103
    Points : 224
    Points
    224
    Par défaut
    Citation Envoyé par the Hound Voir le message
    La première line est inutile : tu crées une nouvelle voiture, que tu leakes après (le pointeur voitureATester est réassigné pour contenir le résultat de ton dynamic_cast<>, donc la voiture que tu as nouvellement créée se retrouve perdue dans le tas).
    La deuxième ligne : c'est là où est ton problème.
    voitureATester est un InterfaceVoiture*.
    Garage::getVoitureAt() renvoie un InterfaceVoiture*.
    Pourquoi caster en Voiture* ? Si tout ce dont tu as besoin c'est d'appeler getVitesseMax() et getMarque(), qui sont définies dans InterfaceVoiture, le cast est inutile. Plus important, imaginons que je crée une classe Camion, qui implémente InterfaceVoiture ; alors si je mets ce camion dans le garage, le dynamic_cast<> va échouer, puisque un Camion n'est pas une Voiture. Par contre, sans le cast, Camion implémentant InterfaceVoiture, je pourrai tout à fait appeler getVitesseMax(), que mon objet soit en réalité un camion ou une voiture.
    Citation Envoyé par Médinoc Voir le message
    En gros, ces deux lignes peuvent être simplement remplacées par:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    voitureATester = garage->getVoitureAt(index);
    Qui lancera une exception std::out_of_bounds pour toute valeur d'index autre que zéro.
    Bonjour,

    Merci à tous les deux pour votre réponse, j'ai enfin compris ! En effet j'étais convaincu qu'écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     InterfaceVoiture* voitureATester = getVoitureAt(index);
    conduisait à une perte de données puisque le véritable type de getVoitureAt(int) est une Voiture*. De plus, pour moi les méthodes getVitesseMax() et getMarque() ne pouvaient pas marcher puisqu'elles étaient appelées à partir d'une interface.

    J'ai tenté de les appeler à partir de voitureATester et comme son véritable type est Voiture*, cela a fonctionné parfaitement ! C'était donc cela, "tester Voiture à travers InterfaceVoiture" ! En fait, c'est vraiment le type de getVoitureAt(int) qui est pris en compte malgré son assignation à une interface !

    Vous avez levé un blocage important chez moi. Encore merci à vous

  20. #20
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Citation Envoyé par Jimmy91 Voir le message
    En effet j'étais convaincu qu'écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     InterfaceVoiture* voitureATester = getVoitureAt(index);
    conduisait à une perte de données puisque le véritable type de getVoitureAt(int) est une Voiture*.
    Tu ne perds pas de données car l'objet réel sur lequel voitureATester pointes n'est pas altéré.
    Si tu copiais l'objet en revanche, tu aurais perdu des données (pas possible ici puisque InterfaceVoiture est abstraite)

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // Code simplifié
    class Vehicule {};
    class Voiture : public Vehicule {};
     
    Voiture maVoiture;
    Vehicule* pv = &maVoiture; // (1)
    Vehicule& rv = maVoiture; // (2)
    Vehicule v = maVoiture; // (3)
    En (1) et (2), pv et rv ne sont pas des objets mais des références (dans le sens général du terme). Comme ce sont des références, *pv et rv réfèrent en réalité à maVoiture. Le type "réel" de *pv et rv est donc Voiture.
    En (3), v est un objet de type Vehicule. Bien qu'on l'initialise avec maVoiture (ce qui provoque un appel au constructeur par copie), v étant un objet, son type réel est celui qui est déclaré, soit un Vehicule. Comme c'est un Vehicule, la copie ne se fait que sur la classe Vehicule, donc tu "perds" les attributs de maVoiture spécifiques à la classe Voiture. Remarque que maVoiture reste en revanche inchangée.

    Dans ton cas, tu renvoies un pointeur sur InterfaceVoiture, tu ne crées pas d'objet InterfaceVoiture. Donc ta voitureATester pointe sur l'objet ayant le type "réel", soit Voiture, Camion, ou autre => tu ne "perds" pas les attributs.

    De plus, pour moi les méthodes getVitesseMax() et getMarque() ne pouvaient pas marcher puisqu'elles étaient appelées à partir d'une interface.
    C'est que tu n'as pas compris le principe des interfaces.
    L'idée d'une interface c'est de dire "j'ai un type X, je veux que X puisse faire ci ou ça, mais je me fiche de savoir comment c'est implémenté".
    Ici X c'est ton InterfaceVoiture, ci c'est getVitesseMax(), et ça c'est getMarque().
    Ce que tu sais, c'est que si tu as une InterfaceVoiture, tu peux appeler ces deux fonctions. Mais que l'objet soit un Camion ou une Voiture, ça ne t'importe pas : tu sais que quelque soit le type réel de cet objet, tu pourras appeler ces deux fonctions, parce que ces deux classes implémentent InterfaceVoiture (donc elles ont forcément défini getVitesseMax() et getMarque()) (*). C'est à ça que sert ton interface : elle rajoute un niveau d'abstraction, et permet d'"unifier" des comportements qui seraient communs à plusieurs types. Ici tu l'as appelé InterfaceVoiture, mais un nom plus approprié serait Vehicule (parce que getMarque(), etc. sont propres aux véhicules et pas juste aux voitures).
    Lorsque tu fais voitureATester->getMarque(), ce sera Voiture::getMarque() qui sera appelé, en admettant que le type réel de l'objet soit une Voiture. Si c'était un Camion, cela aurait été Camion::getMarque(). La fonction correcte à appeler est déterminée à l'exécution, via un mécanisme qu'on appelle le virtual dispatch.

    (*) En C++ une interface est une forme de classe abstraite. Une classe abstraite est une classe qui a au moins une fonction virtuelle pure. Une classe abstraite ne peut pas être instanciée.
    Une classe B héritant d'une classe abstraite A n'est concrète que si elle implémente toutes les fonctions virtuelles pures de ses classes de base (ici, A).
    Le résultat c'est que :
    - On ne peut pas instancier une classe abstraite, et par extension une interface
    - Une référence vers une classe abstraite réfère forcément un objet valide, donc ayant été instancié, donc dont le type réel est concret. Il s'en suit que ce même type implémente forcément la classe abstraite (sinon il ne serait pas concret). Donc on pourra toujours appeler les fonctions définies dans la classe abstraite à partir de cette référence (dans ton cas, on pourra toujours appeler toutes les fonctions de InterfaceVoiture à partir du moment où on a un InterfaceVoiture& ou un InterfaceVoiture* valide).

    Je sais que c'est pas très clair, j'ai fait de mon mieux

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [HTML+CSS] Problème de menu "dynamique"
    Par Invité dans le forum Mise en page CSS
    Réponses: 2
    Dernier message: 06/04/2005, 12h48
  2. Question sur les problèmes d'allocation dynamique
    Par slylafone dans le forum C++
    Réponses: 23
    Dernier message: 25/10/2004, 14h18
  3. [JSP]Problème liste deroulante dynamique
    Par besco dans le forum Servlets/JSP
    Réponses: 1
    Dernier message: 09/09/2004, 17h58
  4. problèmes de textes dynamique dynamiquement générés
    Par stephane eyskens dans le forum Flash
    Réponses: 18
    Dernier message: 05/09/2003, 13h13
  5. [Rave Report] problème de création dynamique
    Par Nivux dans le forum Rave
    Réponses: 2
    Dernier message: 24/05/2003, 00h07

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